Angular & ASP.NET Web API – Uploading Image Binaries to a Database

Overview
In the example, images will be uploaded to a local MDF file table using Angular and ASP.NET C# Web API. This is the easiest solution to understanding how this works. A better example will be added in the coming months.
The Angular UI with Bootstrap will have a form and a table on the right side to display the images. When a new image is added the form will alert the parent component using the @Output() decorator to update the table component using the @ViewChild() decorator. The service uses a Promise instead of an observable for this example.
Not all of the code will be in the page below, so please get the Angular App and Web API from the Github repository https://github.com/fullstacksoup/blog-ng-file-upload-demo. You will have to update the Nuget packages for the API. The API was developed using VS2019. For the UI, please run npm install before ng serve.
Before you can run the API
Try running the following command in the Package Manager Console
use nuget locals all -clear
OR
dotnet nuget locals all --clear
Also may need to update the installed Nuget Packages.
Then Cloning this repo will require you to install the nuget packages. Try an update to get everything installed

MS SQL
First lets create a table in SSMS. You can run the script or use designer.
CREATE TABLE [dbo].[ProfileImages](
[ImageId] [int] IDENTITY(1,1) NOT NULL,
[Label] [varchar](40) NULL,
[Description] [varchar](300) NULL,
[Filename] [varchar](50) NULL,
[Size] [int] NULL,
[URL] [varchar](100) NULL,
[MimeType] [varchar](30) NULL,
[IsActive] [bit] NULL,
[Image] [image] NULL,
CONSTRAINT [PK_ProfileImages] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
Angular
The form will have three fields. Fields for title, description, and file upload field. To make it a bit more interesting we will capture and display the image in separate area along with the file size, mime type, and file name.
Create a class for the file meta data.
class MediaImageClass {
ImageId: number;
Label: string;
Description: string;
Filename: number;
UploadDate: any;
Size: number;
MimeType: string;
Image: any;
}
Create a new instance of the class.
private imageFormData = new MediaImageClass();
Generate a service labeled image-file
ng g s services/image-file
Create two components. On for the form and another for the table
ng g c components/file-form
ng g c components/file-table
In the form Component, import the following:
import { Component, OnInit, OnDestroy, EventEmitter, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
import { ImageFileService } from 'src/app/services/image-file.service';
Create a form as follows.
ngOnInit() {
this.imageForm = this.fb.group({
label: ['', [Validators.required]],
description: [''],
image: [null, [Validators.required]],
});
}
The onSubmit() function will simply call a Promise instead of an Observable. After calling the service, clear out the form and alert the parent that a new record has been added.
onSubmit($event) {
this.imageFormData.AutoId = '1';
this.imageFormData.Title = this.automotiveForm.controls.title.value;
this.imageFormData.Label = this.imageForm.controls.label.value;
this.imageFormData.Description = this.imageForm.controls.description.value;
this.fileSVC.addImageToDB(this.imageFormData);
this.onSubmitForm.emit(this.imageForm);
this.imageForm.reset();
this.removeImage();
this.imageForm.controls.label.setValue('');
this.imageForm.controls.description.setValue('');
this.imageForm.controls.label.setErrors(null);
this.imageForm.controls.description.setErrors(null);
}
HTML Template
<form class="post-form" method="POST" (ngSubmit)="onSubmit($event)" [formGroup]="imageForm" >
<div class="row">
<div class="col-6">
</div>
</div>
<div class="row">
<div class="col-12">
<label>Label</label>
<input type="text" class="form-control" placeholder="Label" name="Label" formControlName="label">
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<label>Description</label>
<textarea class="form-control" formControlName="description"></textarea>
</div>
</div>
<div class="row mt-3">
<div class="col-6">
<div class="custom-file-upload">
<label for="file-upload" class="custom-file-upload1">
<i class="fa fa-cloud-upload"></i> Choose File
</label>
<input #file type="file" id="file-upload" accept='image/*' (change)="preview(file.files)" class="form-control" formControlName="image">
</div>
</div>
</div>
<div class="row mt-3">
<div class="col">
<button class="btn btn-primary" type="submit" [disabled]="!imageForm.valid">Save Image & Title</button>
</div>
</div>
</form>
The image preview section of the HTML template.
<div class="row mu-5">
<div class="col-md-6 text-center">
<img mat-card-image [src]="imgURL" alt="Photo" style="width: 90%; height: 90%" *ngIf="imgURL">
<img mat-card-image [src]="imageURL" alt="Photo" style="width: 90%; height: 90%" <div class="row">
<div class="col-4 text-left">
<div *ngIf="Size !== 0">
File Name: {{Filename}}<br><br>
Size: {{Size}}<br><br>
Format: {{MimeType}}<br><br>
</div>
</div>
<div class="col-5 text-left">
<img mat-card-image [src]="imgURL" alt="Photo" style="height: auto; width: 250px; object-fit: cover;" *ngIf="imgURL">
<img mat-card-image [src]="imageURL" alt="Photo" style="height: auto; width: 250px; object-fit: cover;" *ngIf="!imgURL">
<br>
</div>
</div>
Note: Currently working on calling this as an Observable.
addImageToDB(fileForm: any): string {
const URL = `${environment.baseUrl}/api/userimage/addtodb`;
const formData = new FormData();
// Add the file
formData.append(fileForm.Image.name, fileForm.Image);
// Add file Label and Description
formData.append('Label', fileForm.Label);
formData.append('Description', fileForm.Description);
let status = '';
// Use a promise for this example
const promise = new Promise((resolve, reject) => {
this.http.post(URL, formData)
.toPromise()
.then(
res => { // Success
console.log(res);
status = 'resolved';
}
)
.catch((err) => {
console.error(err);
status = 'rejected';
});
});
return status;
}
ASP.NET
From your ASP.NET Web API Controller please add a method with the route addimageToDB
For attribute routing for the class.
[RoutePrefix("api/userimage")]
public class UserImageController : ApiController
Create a Post Method to record the image.
IMPORTANT: The folder must exist on the server your API is deployed to. In this example APP_DATA is used but it will not exist on the server. You can use “~/Content” instead or add a folder to the app after it’s deployed. Make sure the folder has read write access.
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var ctx = HttpContext.Current;
var root = ctx.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
Getting the image specs with MultipartFileData before saving to the database.
await Request.Content.ReadAsMultipartAsync(provider);
var uniqueFileName = "";
NameValueCollection formdata = provider.FormData;
UserImage imageForm = new UserImage();
imageForm.IsActive = true;
imageForm.Label = formdata["Label"];
imageForm.Description = formdata["Description"];
imageForm.DateCreated = today;
// You can get the file specification from ASP.NET C#
foreach (MultipartFileData file in provider.FileData)
{
var fileName = file.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
string mimeType = file.Headers.ContentType.MediaType;
byte[] documentData = File.ReadAllBytes(file.LocalFileName);
imageForm.ImageData = documentData;
imageForm.MimeType = mimeType;
imageForm.Size = documentData.Length;
}
db.UserImages.Add(imageForm);
db.SaveChanges();
You must be logged in to post a comment.