Last active
March 18, 2021 16:24
-
-
Save stuartaccent/51afc6b17d89d4dc6f3968ede5d789b6 to your computer and use it in GitHub Desktop.
Angular 7 file uploader with queue and progress using HttpClient, example https://stackblitz.com/edit/angular-7-file-upload-queue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="row"> | |
<div class="col-md-3"> | |
<h3>Select files</h3> | |
<input type="file" #fileInput multiple (change)="addToQueue()" /> | |
</div> | |
<div class="col-md-9"> | |
<h3>Upload queue</h3> | |
<table class="table-headed table-striped"> | |
<thead> | |
<tr> | |
<th class="text-left">Name</th> | |
<th class="text-right">Size</th> | |
<th class="text-left">Progress</th> | |
<th class="text-left">Status</th> | |
<th class="text-right">Actions</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr *ngFor="let item of queue | async"> | |
<td>{{ item?.file?.name }}</td> | |
<td class="text-right">{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td> | |
<td>{{ item.progress + ' %' }}</td> | |
<td> | |
<span *ngIf="item.isPending()" class="tag tag-default"></span> | |
<span *ngIf="item.isSuccess()" class="tag tag-success"></span> | |
<span *ngIf="item.inProgress()" class="tag tag-warning"></span> | |
<span *ngIf="item.isError()" class="tag tag-danger"></span> | |
</td> | |
<td class="text-right"> | |
<a tooltip="Upload file" (click)="item.upload()" *ngIf="item.isUploadable()"> | |
<i class="fa fa-upload"></i> | |
</a> | |
<a tooltip="Cancel upload" (click)="item.cancel()" *ngIf="item.inProgress()"> | |
<i class="fa fa-times-circle"></i> | |
</a> | |
<a tooltip="Remove from queue" (click)="item.remove()" *ngIf="!item.inProgress()"> | |
<i class="fa fa-trash"></i> | |
</a> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<div> | |
<a class="button" (click)="uploader.clearQueue()">Clear queue</a> | |
<a class="button button-primary" (click)="uploader.uploadAll()">Upload all</a> | |
</div> | |
</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; | |
import { FileQueueObject, FileUploaderService } from './file-uploader.service'; | |
import { Observable } from 'rxjs'; | |
@Component({ | |
selector: 'file-uploader, [file-uploader]', | |
templateUrl: 'file-uploader.component.html', | |
styleUrls: ['./file-uploader.component.css'] | |
}) | |
export class FileUploaderComponent implements OnInit { | |
@Output() onCompleteItem = new EventEmitter(); | |
@ViewChild('fileInput') fileInput; | |
queue: Observable<FileQueueObject[]>; | |
constructor(public uploader: FileUploaderService) { } | |
ngOnInit() { | |
this.queue = this.uploader.queue; | |
this.uploader.onCompleteItem = this.completeItem; | |
} | |
completeItem = (item: FileQueueObject, response: any) => { | |
this.onCompleteItem.emit({ item, response }); | |
} | |
addToQueue() { | |
const fileBrowser = this.fileInput.nativeElement; | |
this.uploader.addToQueue(fileBrowser.files); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as _ from 'lodash'; | |
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpRequest, HttpResponse } from '@angular/common/http'; | |
import { Injectable, Output } from '@angular/core'; | |
import { BehaviorSubject, Subscription } from 'rxjs'; | |
import { HttpEventType } from '@angular/common/http'; | |
export enum FileQueueStatus { | |
Pending, | |
Success, | |
Error, | |
Progress | |
} | |
export class FileQueueObject { | |
public file: any; | |
public status: FileQueueStatus = FileQueueStatus.Pending; | |
public progress: number = 0; | |
public request: Subscription = null; | |
public response: HttpResponse<any> | HttpErrorResponse = null; | |
constructor(file: any) { | |
this.file = file; | |
} | |
// actions | |
public upload = () => { /* set in service */ }; | |
public cancel = () => { /* set in service */ }; | |
public remove = () => { /* set in service */ }; | |
// statuses | |
public isPending = () => this.status === FileQueueStatus.Pending; | |
public isSuccess = () => this.status === FileQueueStatus.Success; | |
public isError = () => this.status === FileQueueStatus.Error; | |
public inProgress = () => this.status === FileQueueStatus.Progress; | |
public isUploadable = () => this.status === FileQueueStatus.Pending || this.status === FileQueueStatus.Error; | |
} | |
// tslint:disable-next-line:max-classes-per-file | |
@Injectable() | |
export class FileUploaderService { | |
public url: string = 'https://jsonplaceholder.typicode.com/posts'; | |
private _queue: BehaviorSubject<FileQueueObject[]>; | |
private _files: FileQueueObject[] = []; | |
constructor(private http: HttpClient) { | |
this._queue = <BehaviorSubject<FileQueueObject[]>>new BehaviorSubject(this._files); | |
} | |
// the queue | |
public get queue() { | |
return this._queue.asObservable(); | |
} | |
// public events | |
public onCompleteItem(queueObj: FileQueueObject, response: any): any { | |
return { queueObj, response }; | |
} | |
// public functions | |
public addToQueue(data: any) { | |
// add file to the queue | |
_.each(data, (file: any) => this._addToQueue(file)); | |
} | |
public clearQueue() { | |
// clear the queue | |
this._files = []; | |
this._queue.next(this._files); | |
} | |
public uploadAll() { | |
// upload all except already successfull or in progress | |
_.each(this._files, (queueObj: FileQueueObject) => { | |
if (queueObj.isUploadable()) { | |
this._upload(queueObj); | |
} | |
}); | |
} | |
// private functions | |
private _addToQueue(file: any) { | |
const queueObj = new FileQueueObject(file); | |
// set the individual object events | |
queueObj.upload = () => this._upload(queueObj); | |
queueObj.remove = () => this._removeFromQueue(queueObj); | |
queueObj.cancel = () => this._cancel(queueObj); | |
// push to the queue | |
this._files.push(queueObj); | |
this._queue.next(this._files); | |
} | |
private _removeFromQueue(queueObj: FileQueueObject) { | |
_.remove(this._files, queueObj); | |
} | |
private _upload(queueObj: FileQueueObject) { | |
// create form data for file | |
const form = new FormData(); | |
form.append('file', queueObj.file, queueObj.file.name); | |
// upload file and report progress | |
const req = new HttpRequest('POST', this.url, form, { | |
reportProgress: true, | |
}); | |
// upload file and report progress | |
queueObj.request = this.http.request(req).subscribe( | |
(event: any) => { | |
if (event.type === HttpEventType.UploadProgress) { | |
this._uploadProgress(queueObj, event); | |
} else if (event instanceof HttpResponse) { | |
this._uploadComplete(queueObj, event); | |
} | |
}, | |
(err: HttpErrorResponse) => { | |
if (err.error instanceof Error) { | |
// A client-side or network error occurred. Handle it accordingly. | |
this._uploadFailed(queueObj, err); | |
} else { | |
// The backend returned an unsuccessful response code. | |
this._uploadFailed(queueObj, err); | |
} | |
} | |
); | |
return queueObj; | |
} | |
private _cancel(queueObj: FileQueueObject) { | |
// update the FileQueueObject as cancelled | |
queueObj.request.unsubscribe(); | |
queueObj.progress = 0; | |
queueObj.status = FileQueueStatus.Pending; | |
this._queue.next(this._files); | |
} | |
private _uploadProgress(queueObj: FileQueueObject, event: any) { | |
// update the FileQueueObject with the current progress | |
const progress = Math.round(100 * event.loaded / event.total); | |
queueObj.progress = progress; | |
queueObj.status = FileQueueStatus.Progress; | |
this._queue.next(this._files); | |
} | |
private _uploadComplete(queueObj: FileQueueObject, response: HttpResponse<any>) { | |
// update the FileQueueObject as completed | |
queueObj.progress = 100; | |
queueObj.status = FileQueueStatus.Success; | |
queueObj.response = response; | |
this._queue.next(this._files); | |
this.onCompleteItem(queueObj, response.body); | |
} | |
private _uploadFailed(queueObj: FileQueueObject, response: HttpErrorResponse) { | |
// update the FileQueueObject as errored | |
queueObj.progress = 0; | |
queueObj.status = FileQueueStatus.Error; | |
queueObj.response = response; | |
this._queue.next(this._files); | |
} | |
} |
Hi everyone...I am a beginner... Thanks a lot to Mr. Stuartaccent for your awesome code.. I have download your code and it works fine... When i change the URL to my cloud domain's URL it is showing this error.... "HttpErrorResponse {headers: {…}, status: 0, statusText: "Unknown Error",".. I have tried with localhost which has xamp server too. Do i need to run any code in my server part? If so what code should i run? Thanks in advance....
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @ganeshmuthuvelu,
sorry for the massive delay. Did u ever find out what this was? there is no limit that i know of.
The only thing that springs to mind that does have a 100mg limit for a request is Cloudflare.