Keywords: Angular File Upload | FormData API | HTTP Client
Abstract: This article provides a comprehensive guide to implementing file upload functionality in Angular, covering everything from basic file selection to advanced features like progress monitoring and cancellation. By analyzing implementations in both Angular 2 and Angular 5, and combining FormData API with HTTP client, it offers complete code examples and best practices. The article also discusses building user-friendly upload interfaces, handling multiple file uploads, and backend integration solutions.
Fundamental Principles of File Upload
Implementing file upload functionality in web applications centers around understanding the standard file input element provided by browsers. The HTML <input type="file"> element allows users to select files from their local file system, with JavaScript used to obtain file references for subsequent processing.
When a user selects a file, the browser triggers a change event, and developers can access the selected file list through the event object's target.files property. This file list is a FileList object containing one or more File objects, each of which includes file metadata (such as name, size, type) and the actual content.
Basic File Upload Implementation in Angular 2
In Angular 2, file upload can be implemented using the native HTTP module without relying on third-party libraries. Here's a complete implementation example:
<input type="file" (change)="fileChange($event)" placeholder="Upload file" accept=".pdf,.doc,.docx">
Corresponding component class implementation:
fileChange(event) {
let fileList: FileList = event.target.files;
if (fileList.length < 1) {
return;
}
let file: File = fileList[0];
let formData:FormData = new FormData();
formData.append('uploadFile', file, file.name)
let headers = new Headers();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = new RequestOptions({ headers: headers });
this.http.post(`${this.apiEndPoint}`, formData, options)
.map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log('success'),
error => console.log(error)
);
}
This implementation demonstrates the core steps of file upload: obtaining file references, building FormData, setting HTTP headers, and sending POST requests. Note that in newer versions of Angular, setting Content-Type to multipart/form-data might be automatically handled by the browser, and manual setting could potentially cause issues.
Modern Implementation in Angular 5 and Later Versions
As Angular versions evolve, the implementation of file upload has also improved. Angular 5 introduced the more powerful HttpClient, providing better type safety and feature support:
import { Injectable } from '@angular/core';
import {HttpClient, HttpParams, HttpRequest, HttpEvent} from '@angular/common/http';
import {Observable} from "rxjs";
@Injectable()
export class UploadService {
constructor(private http: HttpClient) { }
uploadFile(url: string, file: File): Observable<HttpEvent<any>> {
let formData = new FormData();
formData.append('upload', file);
let params = new HttpParams();
const options = {
params: params,
reportProgress: true,
};
const req = new HttpRequest('POST', url, formData, options);
return this.http.request(req);
}
}
Using this service in a component:
uploadFile(files: FileList) {
if (files.length == 0) {
console.log("No file selected!");
return
}
let file: File = files[0];
this.upload.uploadFile(this.appCfg.baseUrl + "/api/flash/upload", file)
.subscribe(
event => {
if (event.type == HttpEventType.UploadProgress) {
const percentDone = Math.round(100 * event.loaded / event.total);
console.log(`File is ${percentDone}% loaded.`);
} else if (event instanceof HttpResponse) {
console.log('File is completely loaded!');
}
},
(err) => {
console.log("Upload Error:", err);
}, () => {
console.log("Upload done");
}
)
}
Building User-Friendly Upload Interfaces
Native file input elements have styling limitations, so in practical applications, custom upload interfaces are typically built. The core strategy involves hiding the native file input and triggering file selection through custom UI elements:
<input type="file" #fileUpload style="display: none" (change)="onFileSelected($event)">
<div class="file-upload">
<button (click)="fileUpload.click()">Select File</button>
</div>
This pattern allows developers to fully control the appearance and interaction of the upload interface while leveraging the browser's native file selection functionality.
Progress Monitoring and Cancellation Features
Modern file uploads typically require progress feedback and cancellation capabilities. By setting reportProgress: true and observing the HTTP event stream, precise upload progress monitoring can be achieved:
uploadSub: Subscription;
uploadProgress: number = 0;
uploadFile(file: File) {
const formData = new FormData();
formData.append('file', file);
const req = new HttpRequest('POST', '/upload', formData, {
reportProgress: true,
observe: 'events'
});
this.uploadSub = this.http.request(req).subscribe(
event => {
if (event.type === HttpEventType.UploadProgress) {
this.uploadProgress = Math.round(100 * event.loaded / event.total);
} else if (event.type === HttpEventType.Response) {
console.log('Upload completed');
this.resetUpload();
}
},
error => {
console.error('Upload failed', error);
this.resetUpload();
}
);
}
cancelUpload() {
if (this.uploadSub) {
this.uploadSub.unsubscribe();
this.resetUpload();
}
}
resetUpload() {
this.uploadProgress = 0;
this.uploadSub = null;
}
File Type Restrictions and Multiple File Upload
Using the accept attribute, users can be restricted to selecting only specific file types:
<input type="file" accept=".png,.jpg,.jpeg">
For multiple file uploads, the multiple attribute can be used:
<input type="file" multiple (change)="handleMultipleFiles($event)">
When handling multiple files, the FileList needs to be iterated through:
handleMultipleFiles(event) {
const files: FileList = event.target.files;
for (let i = 0; i < files.length; i++) {
this.uploadFile(files[i]);
}
}
Backend Integration Considerations
Files uploaded from the frontend need to be processed on the backend. Using Node.js and Express as an example, the express-fileupload middleware can be employed:
const fileUpload = require('express-fileupload');
app.use(fileUpload());
app.post('/upload', (req, res) => {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
const uploadedFile = req.files.uploadFile;
uploadedFile.mv('/path/to/save/' + uploadedFile.name, (err) => {
if (err) {
return res.status(500).send(err);
}
res.send('File uploaded!');
});
});
Best Practices and Considerations
When implementing file upload functionality, consider the following best practices:
- Security: Validate file types and sizes on the backend to prevent malicious file uploads
- User Experience: Provide clear progress feedback and error handling
- Performance: For large files, consider chunked uploads or resumable uploads
- Compatibility: Ensure proper functioning across different browsers and devices
- Error Handling: Properly handle network errors, server errors, and user cancellations
By combining Angular's powerful features with modern browser APIs, feature-rich and user-friendly file upload solutions can be built.