Upload file with data by clicking on button or by drag and drop, is the topic of this article. We will try to create an image gallery by uploading files and save data into database and images into a folder. We will try to create a component in a way that it can be used anywhere in entire application and configure properties like file size, type of files, single or multiple files, maximum number of files a user can upload at a time.
Create Angular 4 CLI application by using
ng new ImageGallery --ng4
Open Package.json and add bootstrap to design this project:
"bootstrap": "^3.3.7"
// Open ImageGallery folder and run
npm install
// Open Angular-cli.json file and add in styles
"styles": [
"styles.css",
"../node_modules/bootstrap/dist/css/bootstrap.min.css"
],
Let's see we want to give the feature to configure the control for different validation like
At the same time we want to pass some values, because when we will be uploading a file it should be linked with some records, for this say we want to use the project Id and Section Id, we can use any number of parameters
Create a new component names FileUpload.Component by using command:
ng generate component FileUpload
Open file file-upload.component.html and add following code in it
<div draggable="true" ngClass="{{dragAreaClass}}">
<div class="row">
<div class="col-md-12 text-center" >
<a href="javascript:void(0)" (click)="file.click()" >
Click to browse
</a>
Or Drag & Drop to upload your files
<input type="file"
#file
[multiple]="(maxFiles > 1)"
(change) = "onFileChange($event)"
style="display:none" />
</div>
</div>
</div>
<div class="row error" *ngIf="errors.length > 0">
<ul>
<li *ngFor="let err of errors">{{err}}</li>
</ul>
</div>
This the entire html to make a component to upload file by clicking and by drag and drop.
Let's see what this code is:
file-upload.component.ts
We will see this file part by part, open this file and first import some decorators:
import { Component, OnInit, Input, Output, EventEmitter, HostListener } from '@angular/core';
import {FileService} from '../../services/file.service';
@Component({
selector: 'app-file-upload',
templateUrl: './file-upload.component.html'
})
We imported here everything we need to use for drag and drop, input parameter, output to notify the user the status of upload and register different listeners for drag and drop.
We need some variable for our upload component which we are going to add at the beginning of the class:
export class FileUploadComponent implements OnInit {
errors: Array<string> =[];
dragAreaClass: string = 'dragarea';
@Input() projectId: number = 0;
@Input() sectionId: number = 0;
@Input() fileExt: string = "JPG, GIF, PNG";
@Input() maxFiles: number = 5;
@Input() maxSize: number = 5; // 5MB
@Output() uploadStatus = new EventEmitter();
constructor(private fileService: FileService) { }
ngOnInit() { }
..........
}
Let's see why we need these variables, @Input is used to configure the component different properlies and @output to emit the upload status:
Add the method file change to upload the file on clicking and selecting the file, we already added this method name in our html onFileChange($event)
onFileChange(event){
let files = event.target.files;
this.saveFiles(files);
}
For now don't worry about the this.saveFiles method, we will add it latter in this article.
Now we will add different methods for drag and drop: onDragOver: add this method to change the css class when user drag over to drop area
@HostListener('dragover', ['$event']) onDragOver(event) {
this.dragAreaClass = "droparea";
event.preventDefault();
}
onDragEnter: add this method to change the css class when user enter to drop area
@HostListener('dragenter', ['$event']) onDragEnter(event) {
this.dragAreaClass = "droparea";
event.preventDefault();
}
onDragEnd: add this method to change the css class, it is called when drag operation is being ended
@HostListener('dragend', ['$event']) onDragEnd(event) {
this.dragAreaClass = "dragarea";
event.preventDefault();
}
onDragLeave: add this method to change the css class, it is called when goes out of the drag and drop area, so need to change the area to normal now.
@HostListener('dragleave', ['$event']) onDragLeave(event) {
this.dragAreaClass = "dragarea";
event.preventDefault();
}
onDrop: This is the event which is called when user drop files to our drag and drop area and now we need to check the dropped files and call the save method to save files
@HostListener('drop', ['$event']) onDrop(event) {
this.dragAreaClass = "dragarea";
event.preventDefault();
event.stopPropagation();
var files = event.dataTransfer.files;
this.saveFiles(files);
}
Note: In all our drag & drop events we use the preventDefault
, it is important otherwise file will try to open in the browser.
Save File method, we used here because in both type of upload, either by clicking and selecting or by drag and drop case we use the same method to upload the files:
saveFiles(files){
this.errors = []; // Clear error
// Validate file size and allowed extensions
if (files.length > 0 && (!this.isValidFiles(files))) {
this.uploadStatus.emit(false);
return;
}
if (files.length > 0) {
let formData: FormData = new FormData();
for (var j = 0; j < files.length; j++) {
formData.append("file[]", files[j], files[j].name);
}
var parameters = {
projectId: this.projectId,
sectionId: this.sectionId
}
this.fileService.upload(formData, parameters)
.subscribe(
success => {
this.uploadStatus.emit(true);
console.log(success)
},
error => {
this.uploadStatus.emit(true);
this.errors.push(error.ExceptionMessage);
})
}
}
This part of the code is main to upload the file with data, what this code will do:
parameters
to pass extra data to the Service/Web APIupload
method to upload filesisValidFiles: This method will check the different types of validations like extension, size and number of files:
private isValidFiles(files){
// Check Number of files
if (files.length > this.maxFiles) {
this.errors.push("Error: At a time you can upload only " + this.maxFiles + " files");
return;
}
this.isValidFileExtension(files);
return this.errors.length === 0;
}
See first we check the number of files to upload is greater to the number of files allowed, then we use another method isValidFileExtension which check every file extension
private isValidFileExtension(files){
// Make array of file extensions
var extensions = (this.fileExt.split(','))
.map(function (x) { return x.toLocaleUpperCase().trim() });
for (var i = 0; i < files.length; i++) {
// Get file extension
var ext = files[i].name.toUpperCase().split('.').pop() || files[i].name;
// Check the extension exists
var exists = extensions.includes(ext);
if (!exists) {
this.errors.push("Error (Extension): " + files[i].name);
}
// Check file size
this.isValidFileSize(files[i]);
}
}
First we converted the fileExt string to an array and then convert to lower case and then keep checking one by one all the file extensions. At the end we call another method isValidFileSize which will check for the file size
private isValidFileSize(file) {
var fileSizeinMB = file.size / (1024 * 1000);
var size = Math.round(fileSizeinMB * 100) / 100; // convert upto 2 decimal place
if (size > this.maxSize)
this.errors.push("Error (File Size): " + file.name + ": exceed file size limit of " + this.maxSize + "MB ( " + size + "MB )");
}
Before writing the service code in Angular, let's add Web API code first then we can come back and complete our service code.
We will upload the file in a folder and save record into database, here is the list of properties we will save into database, so create a table named "Files":
CREATE TABLE [Files](
[Id] [int] NOT NULL PRIMARY KEY IDENTITY(1,1),
[FileName] [varchar](200) NOT NULL,
[FileSize] [int] NOT NULL,
[ImagePath] [varchar](200) NOT NULL,
[ThumbPath] [varchar](200) NOT NULL,
[ProjectId] [int] NOT NULL,
[SectionId] [int] NOT NULL
)
Here the complete code in Web API to save the file into folder as well as save into database
[HttpPost]
public async Task<HttpResponseMessage> Upload(int projectId, int sectionId)
{
var status = new MyReponse();
try
{
var context = HttpContext.Current.Request;
if (context.Files.Count > 0)
{
var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();
var index = 0;
foreach (var streamContent in filesReadToProvider.Contents)
{
var fileBytes = await streamContent.ReadAsByteArrayAsync();
var file = new File();
file.ProjectId = projectId;
file.SectionId = sectionId;
file.FileName = context.Files[index].FileName;
file.FileSize = fileBytes.Length;
file.ImagePath = String.Format("/UploadedFiles/{0}_{1}_{2}", projectId, sectionId, file.FileName);
file.ThumbPath = String.Format("/UploadedFiles/{0}_{1}_th_{2}", projectId, sectionId, file.FileName);
var img = Image.FromStream(new System.IO.MemoryStream(fileBytes));
await SaveFiles(file, img);
index++;
}
status.Status = true;
status.Message = "File uploaded successfully";
return Request.CreateResponse(HttpStatusCode.OK, status);
}
}
catch (Exception ex)
{
status.Message = ex.Message;
}
return Request.CreateResponse(HttpStatusCode.OK, status);
}
Let's see this code and explanation
I will upload this Web API file on github so you can easily create your Web API method.
To use this Web API method we need a service so create a folder "services" in src folder and create a service named FileService by using command:
ng generate service File
Add following code in it to save the file and get the uploaded file detail:
import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
@Injectable()
export class FileService {
_baseURL: string = 'http://localhost:xxxx/api/fileupload/'
constructor(private http: Http) { }
upload(files, parameters){
let headers = new Headers();
let options = new RequestOptions({ headers: headers });
options.params = parameters;
return this.http.post(this._baseURL + 'upload', files, options)
.map(response => response.json())
.catch(error => Observable.throw(error));
}
getImages(){
return this.http.get(this._baseURL + "getimages")
.map(response => response.json())
.catch(error => Observable.throw(error));
}
}
It has two method upload and getImages, let see these two methods in detail:
We are done, let's add this component to the app.component.html to upload the file and use some hard coded value for project id and section id:
<div class="row">
<div class="col-md-12">
<app-file-upload
projectId="100"
sectionId="107"
maxFiles="4"
maxSize="2"
fileExt="JPG, GIF, PNG"
(uploadStatus)="refreshImages($event)" >
</app-file-upload>
</div>
</div>
As you can see here everything is configurable so this component can be used for any number of files, any size of file, any type of files.
We use here refreshImages method to chech the upload status and refresh the images on the page.
refreshImages(status){
if (status == true){
console.log( "Uploaded successfully!");
this.getImageData();
}
}
How to show the images on the page
<div class="well">
<img *ngFor="let img of images"
[src]="'http://localhost:xxxx' + img.thumbPath"
class="img-thumbnail"
alt="">
</div>
In app.component.ts add code to get images getImageData()
from the database by using Web API method, call this method OnInit and when we get notification from upload component that upload completed:
import { Component, OnInit } from '@angular/core';
import { FileService } from '../services/file.service';
@Component({....})
export class AppComponent implements OnInit {
....
constructor(private fileService: FileService) { }
ngOnInit(){ this.getImageData(); }
getImageData(){
this.fileService.getImages().subscribe(
data =>{ this.images = data.result},
error => this.errorMessage = error
)
}
......
}
CSS used for this drag and drop and error:
.error{ color: #f00; }
.dragarea{
font-size: 24px;
border: 3px solid #bbb;
padding: 20px ;
background-color: #fff;
color: #bbb;
}
.droparea{
font-size: 24px;
border: 3px dashed #bbb;
padding: 20px ;
background-color: #eff;
color: #aaa;
}
Just uploaded the src folder from my Image Gallery application to github - please download from there for more detail, also added complete Web API controller with name FileUploadController.cs, hope it will help to understand both part.
![]() |
Having 13+ years of experience in Microsoft Technologies (C#, ASP.Net, MVC and SQL Server). Worked with Metaoption LLC, for more than 9 years and still with the same company. Always ready to learn new technologies and tricks.
|
By Ali Adravi On 24 Sep, 17 Viewed: 40,971 |
@Input and @Output decorator ar used to pass data/communicate between components, @input is used to pass data from parent to child component on the other hand @Output is use to notify the parent by using EventEmitter. Take the simple example of customers list parent page and add/edit child... By Ali Adravi On 30 Sep 2017 Viewed: 2,107
Angular 2 insert update, delete, search, sort feature we will try in this article which we will improve with dynamic component loading and image upload with drag and drop feature in coming articles with the same base code. We are going to use Angular-CLI 4 for this application. What we need to... By Ali Adravi On 03 Sep 2017 Viewed: 25,716
Angular 2 gives full control to add or remove component in other component programmatically by simple steps. In this article we are going to see how to add, remove experience component to any other component, we will also try to pass some values to component from parent to child component to see if... By Ali Adravi On 27 Aug 2017 Viewed: 50,293
Createing accordion demo in Angular 2 is more than easy, Angular have rich of conditional features which we will see in this article, like how to apply more than one class on the basis of some conditional values. For this demo we are not goign to write some fancy CSS and animation but use the... By Ali Adravi On 19 May 2017 Viewed: 11,878
Angular 2 have a built-in service called Title, while in angularjs or angular 1 we were maintaining it through the routing or through the service. By using the Title service provided by Angular 2, in 2 minute every thing will be set without any complication, even we don't need to understand the... By Ali Adravi On 14 Apr 2017 Viewed: 5,024