In this article, I will show you how to upload multiple files from Angular to Dotnet core web API and saving them to the SQL Server database. I will also show you how to fetch those files from the database and download them on the user’s browser.
I will be using the Angular template of Dotnet core web application project. For saving and retrieving data from the database I will be using Entity framework core. So let’s start
Angular changes for uploading files
Create a dotnet core web application project with an Angular template. Add the following 2 models inside your Angular project.
export class Document {
DocumentId: number;
DocumentName: string;
DocumentContent: string;
ContentType: string;
}
import { Document } from './Document';
export class DocumentList {
constructor(
public DocumentList: Document[],
) { }
}
Create service
Create a service named “Document”. You can give it any name and add the following code to it. Here we have defined UploadDocument() method which is used to post uploaded files to the server.
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { DocumentList } from '../Models/DocumentList';
@Injectable({
providedIn: 'root'
})
export class DocumentService {
BaseUrl: string;
constructor(private http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
this.BaseUrl = baseUrl;
}
UploadDocument(DocumentList: DocumentList) {
return this.http.post<any>(this.BaseUrl + 'api/Document/DocumentUpload', DocumentList);
}
}
Create component for uploading files from Angular
Create a component named “Document”. You can give it any name.
Paste the following HTML code in the HTML file of your component. We will allow a minimum of 1 file and a maximum of 5 files to upload. The size of every file should not be more than 1 MB. When user selects the file, handleFileInput() method of our component is called.
On click of Upload button onSubmit() method of our component is called. After the files are uploaded to the server, we are showing the list of uploaded files to the user. By clicking on those files they can download them.
<div class="form-group">
<label for="name">Choose Documents <span class="color-red">(Upto 5 files and size should be less than 1 MB) :</span></label><br />
<input type="file" id="FileInput" class="form-control-file" #Docs (change)="handleFileInput($event)" multiple="multiple">
<div class="color-red" *ngIf="Docs.files.length < 1 || Docs.files.length > 5">Please Add at least 1 documents and maximum 5</div>
</div>
<button type="button" (click)="onSubmit()">Upload</button>
<div>
<ul>
<li *ngFor="let doc of DocListToShow">
<a href="api/Document/DownloadDocument/{{ doc.DocumentId }}">{{ doc.DocumentName }}</a>
</li>
</ul>
</div>
CSS changes for the component
Add the following css code to css file of your component.
.color-red {
color: red;
}
Add the following properties to your component class.
UploadedFiles: any[] = [];
Documents: Document[] = [];
MaxFileSize: number = 1048576;
DocListToShow: any;
Inject DocumentService in your component.
constructor(private DocumentService: DocumentService) { }
Add method to handle file input
The below method will be called when the user selects files to upload from the file input HTML element. In this method, we are first checking whether at least 1 and a maximum of 5 files are selected. If not, we are showing an alert.
If yes, we are looping through the uploaded files and pushing them inside our UploadedFiles array only if they are less than 1 MB in size.
Then we are looping through the UploadedFiles array. We are reading details of uploaded files like name, type, content and pushing it in another Documents array, which will be sent to the server.
handleFileInput(event) {
//clear existing added files
this.UploadedFiles = [];
this.Documents = [];
if (event.target.files && event.target.files.length > 0 && event.target.files.length < 6) {
//user can add upto 5 files
for (let index = 0; index < event.target.files.length; index++) {
if (event.target.files[index].size < this.MaxFileSize) {
// Add file to list of files if file is less then 1 MB
this.UploadedFiles.push(event.target.files[index]);
}
else {
alert('File size should be less than 1 MB');
}
}
}
else {
alert('Please enter minimum 1 and maximum 5 files');
}
for (let index = 0; index < this.UploadedFiles.length; index++) {
let Doc = new Document();
//Setting Document information
Doc.DocumentName = this.UploadedFiles[index].name;
Doc.ContentType = this.UploadedFiles[index].type;
let fileReader = new FileReader();
fileReader.onload = () => {
//pushing Document in the Documents array once file is uploaded
Doc.DocumentContent = fileReader.result.toString();
this.Documents.push(Doc);
}
// Reading data from file to Filereader
fileReader.readAsDataURL(this.UploadedFiles[index]);
}
}
When the upload button is clicked by the user, the below method will be called. Here we are first checking whether any files exists or not in the Documents array. If yes, we are sending it to the server. The server is returning us the list of files uploaded which we are displaying to the user.
onSubmit() {
if (this.Documents.length > 0) {
this.DocumentService.UploadDocument(new DocumentList(this.Documents)).subscribe(
data => {
this.UploadedFiles = [];
this.Documents = [];
alert(data.message);
this.DocListToShow = data.Data;
},
error => {
this.UploadedFiles = [];
this.Documents = [];
alert(error);
});
}
else {
alert("please enter document first.");
}
}
The final component class
The final component class looks like this
import { Component, OnInit } from '@angular/core';
import { DocumentService } from '../Services/document.service';
import { Document } from '../Models/Document';
import { DocumentList } from '../Models/DocumentList';
@Component({
selector: 'app-document',
templateUrl: './document.component.html',
styleUrls: ['./document.component.css']
})
export class DocumentComponent implements OnInit {
UploadedFiles: any[] = [];
Documents: Document[] = [];
MaxFileSize: number = 1048576;
DocListToShow: any;
constructor(private DocumentService: DocumentService) { }
ngOnInit() {
}
handleFileInput(event) {
//clear existing added files
this.UploadedFiles = [];
this.Documents = [];
if (event.target.files && event.target.files.length > 0 && event.target.files.length < 6) {
//user can add upto 5 files
for (let index = 0; index < event.target.files.length; index++) {
if (event.target.files[index].size < this.MaxFileSize) {
// Add file to list of files if file is less then 1 MB
this.UploadedFiles.push(event.target.files[index]);
}
else {
alert('File size should be less than 1 MB');
}
}
}
else {
alert('Please enter minimum 1 and maximum 5 files');
}
for (let index = 0; index < this.UploadedFiles.length; index++) {
let Doc = new Document();
//Setting Document information
Doc.DocumentName = this.UploadedFiles[index].name;
Doc.ContentType = this.UploadedFiles[index].type;
let fileReader = new FileReader();
fileReader.onload = () => {
//pushing Document in the Documents array once file is uploaded
Doc.DocumentContent = fileReader.result.toString();
this.Documents.push(Doc);
}
// Reading data from file to Filereader
fileReader.readAsDataURL(this.UploadedFiles[index]);
}
}
onSubmit() {
if (this.Documents.length > 0) {
this.DocumentService.UploadDocument(new DocumentList(this.Documents)).subscribe(
data => {
this.UploadedFiles = [];
this.Documents = [];
alert(data.message);
this.DocListToShow = data.Data;
},
error => {
this.UploadedFiles = [];
this.Documents = [];
alert(error);
});
}
else {
alert("please enter document first.");
}
}
}
Add path to your component in your routing file.
{ path: 'UploadDocument', component: DocumentComponent },
SQL Server changes for uploading files from angular
Create a database in SQL Server and create a table in that database with following structure.
CREATE TABLE [dbo].[Documents](
[DocumentId] [int] IDENTITY(1,1) NOT NULL,
[DocumentName] [nvarchar](100) NOT NULL,
[DocumentContent] [varbinary](max) NOT NULL,
[ContentType] [nvarchar](100) NOT NULL,
CONSTRAINT [PK_Documents] PRIMARY KEY CLUSTERED
(
[DocumentId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
How to store files in SQL Server database ?
For storing files in database, you have 2 options
1) varbinary column – If your file size is less than 1 MB then you can use this option. It is not recommended for large-size files.
2) FILESTREAM – For file size greater than 1MB you should use FILESTREAM attribute of SQL Server.
Check out this answer for a better understanding. In this tutorial, we are only allowing files that are less than 1 MB in size. So I am using [varbinary](max) to store the contents of the file.
Dotnet Core changes for uploading files from angular
Add entity framework core in your project. I have used the database first approach in which entity framework creates models itself. The following model is created by entity framework for the corresponding table in the database which we created earlier.
public partial class Document
{
public int DocumentId { get; set; }
public string DocumentName { get; set; }
public byte[] DocumentContent { get; set; }
public string ContentType { get; set; }
}
Create the following ViewModels in your Dotnet core project to send and receive data from client.
public class DocumentListVM
{
public DocumentVM[] DocumentList { get; set; }
}
public class DocumentVM
{
public string DocumentName { get; set; }
public string ContentType { get; set; }
public string DocumentContent { get; set; }
}
Add action in the controller to upload files
Create a controller in your Dotnet core project. Add the following action method in that controller.
In this method, We are storing the files received from the client into the database using entity framework. We are also returning the uploaded files back to the client to display to the user. So that user will be able to download the files.
[HttpPost("DocumentUpload")]
public async Task<IActionResult> DocumentUpload(DocumentListVM DocumentInputModel)
{
try
{
DocumentDemoContext db = new DocumentDemoContext();
List<Document> DocList = GetDocList(DocumentInputModel.DocumentList);
await db.Documents.AddRangeAsync(DocList);
int NoOfRowsInserted = await db.SaveChangesAsync();
if (NoOfRowsInserted > 0)
{
return Ok(new { message = "Documents Saved Successfully", Data = DocList });
}
else
{
return Ok(new { message = "Something went wrong. Please try again." });
}
}
catch (Exception ex)
{
return Ok(new { message = ex.Message });
}
}
In the following method, we are assigning values from our ViewModel to the database model. We are creating a list of Documents that can be directly stored in the database.
The DocumentContent sent from FileReader in Angular is like this
data:application/pdf;base64,JVBERi0xLjQKJbC4upUKMSAwIG9iajw8L1R5cGUvQ2F0YWxvZy9QYWd...
The actual document content starts after the comma(,). That is why in the following method before assigning the document content to our database model. We are partitioning the document content from the doctype info.
We are also converting our DocumentContent string to byte[] array. So that it is compatible with our database and can be stored in varbinary column.
private List<Document> GetDocList(DocumentVM[] lstDocVM)
{
//converting document array received to database document table list
List<Document> DBDocList = new List<Document>();
foreach (var Doc in lstDocVM)
{
// dividing file content from file type
Doc.DocumentContent = Doc.DocumentContent.Substring(Doc.DocumentContent.IndexOf(",") + 1);
DBDocList.Add(new Document
{
DocumentName = Doc.DocumentName,
DocumentContent = Convert.FromBase64String(Doc.DocumentContent),
ContentType = Doc.ContentType
});
}
return DBDocList;
}
When files are uploaded to the database, we are returning the list of uploaded files to the user. This list of files is displayed to the user. As shown in the image

Add Action to download uploaded files
When user clicks on those file names. The following action method will be called with the DocumentId sent along with it. We are fetching the document based on documentId from the table and returning it to the user.
[HttpGet("DownloadDocument/{DocumentId}")]
public IActionResult DownloadDoument(long DocumentId)
{
try
{
DocumentDemoContext db = new DocumentDemoContext();
Document doc = db.Documents.FirstOrDefault(x => x.DocumentId == DocumentId);
return File(doc.DocumentContent, doc.ContentType, doc.DocumentName);
}
catch (Exception ex)
{
return Ok(new { message = ex.Message });
}
}
The final controller class looks like this
using DocumentDemo.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DocumentDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class DocumentController : ControllerBase
{
[HttpPost("DocumentUpload")]
public async Task<IActionResult> DocumentUpload(DocumentListVM DocumentInputModel)
{
try
{
DocumentDemoContext db = new DocumentDemoContext();
List<Document> DocList = GetDocList(DocumentInputModel.DocumentList);
await db.Documents.AddRangeAsync(DocList);
int NoOfRowsInserted = await db.SaveChangesAsync();
if (NoOfRowsInserted > 0)
{
return Ok(new { message = "Documents Saved Successfully", Data = DocList });
}
else
{
return Ok(new { message = "Something went wrong. Please try again." });
}
}
catch (Exception ex)
{
return Ok(new { message = ex.Message });
}
}
private List<Document> GetDocList(DocumentVM[] lstDocVM)
{
//converting document array received to database document table list
List<Document> DBDocList = new List<Document>();
foreach (var Doc in lstDocVM)
{
// dividing file content from file type
Doc.DocumentContent = Doc.DocumentContent.Substring(Doc.DocumentContent.IndexOf(",") + 1);
DBDocList.Add(new Document
{
DocumentName = Doc.DocumentName,
DocumentContent = Convert.FromBase64String(Doc.DocumentContent),
ContentType = Doc.ContentType
});
}
return DBDocList;
}
[HttpGet("DownloadDocument/{DocumentId}")]
public IActionResult DownloadDoument(long DocumentId)
{
try
{
DocumentDemoContext db = new DocumentDemoContext();
Document doc = db.Documents.FirstOrDefault(x => x.DocumentId == DocumentId);
return File(doc.DocumentContent, doc.ContentType, doc.DocumentName);
}
catch (Exception ex)
{
return Ok(new { message = ex.Message });
}
}
}
}
When Dotnet core returns data from the server to the client. It automatically converts the first letter of each property to lowercase. To avoid that add the following code in ConfigureServices method after services.AddControllersWithViews().
services.AddControllersWithViews().AddJsonOptions(opts => opts.JsonSerializerOptions.PropertyNamingPolicy = null);
Now you can test the application. The final application looks like this


The code for this tutorial is available on GitHub at https://github.com/juzer-hakimji/Document-Upload-Demo.
You can download the SQL Server database from here.
Thank you for reading this article. Have a good day.
Please check out this free player auction software for players participating in local sports tournaments and also share with others Thanks.
Share :
Hi,
Hope you liked the post, Thanks for reading 🙂