Angular / RxJs: Download Progress
I have working solution to getting download progress in Angular 13 / RxJs 7.
First I defined some enums:
export enum RequestType {
get = 'GET',
post = 'POST',
put = 'PUT',
delete = 'DELETE',
}
export enum ActionType {
download = 1,
upload
}
Next, I implemented a shared service to handle tracking progress:
@Injectable({
providedIn: 'root'
})
export class DownloadProgressService {
percentDone = 0;
actionType: ActionType;
constructor(private http: HttpClient) { }
downloadFile(uri: string, requestType: RequestType, actionType: ActionType, body: any): Observable<number> {
this.actionType = actionType;
const req = new HttpRequest(requestType, uri, body, {
reportProgress: true,
responseType: 'blob'
});
return this.http
.request(req)
.pipe(
map(event => this.getPercentage(event)),
);
}
public getPercentage(event: HttpEvent<any>): number {
switch (event.type) {
case HttpEventType.UploadProgress:
// Downloading files will still trigger upload progress due to the back-and-forth, ignore this
if (this.actionType !== ActionType.upload) {
return 0;
}
// Compute and show the % done:
if (event && event.total) {
this.percentDone = Math.round(100 * event.loaded / event.total);
return this.percentDone;
} else {
return 0;
}
case HttpEventType.DownloadProgress:
if (event && event.total) {
this.percentDone = Math.round(100 * event.loaded / event.total);
}
return this.percentDone;
default:
// Not an event we care about
return this.percentDone;
}
}
}
Then, I simply subscribe to this and get proper progress of the download:
this.downloadService
.downloadFile(url, RequestType.post, ActionType.download, body)
.subscribe(progress => this.progress = progress);
This all works great, I get a progress bar with my component library that shows real progress.
The question is ... how do I get the resulting file?
Instead of returning only the progress you could return an object that contains the progress percentage and the HTTP response.
return this.http.request(req).pipe(
map(event => ({
progress: this.getPercentage(event),
response: event
})),
);
Now in the subscription you can you use the object
this.downloadService
.downloadFile(url, RequestType.post, ActionType.download, body)
.subscribe({
next: ({progress, response}) => {
this.progress = progress;
// use response
},
error: (error: any) => {
// handle errors
}
});