Angular | Upload Multiple Files and Capture Upload Status and the Progress of each
Hello ! :) Before jump in to the deep discussion, will start like this. So I have a requirement as below,
User can select multiple files → Upload Files → Show progress of each File and the Upload Status (Successfully uploaded or not).
Here in the requirement, the main thing is, all the files are upload at the same time, but if there’s any error, it should not impact to the rest of the flow.
So here, File Upload is something more common, so here in this article I am not gonna discuss the basic skeleton of that. So, here I mainly focus to discuss
- How to upload list of the files parallelly in Angular
- How to capture progress of each file
- How to show the final status of each file
- How to handle errors of each file to not to break the flow
Note — As a mock file upload server I use here, ConvertAPI
Let’s go !
Prerequisites
- Fundamental understanding of Angular | RxJS
- Sample angular project with basic Skeleton of File Upload
So, for our discussion, I have already set up an sample angular application with File Upload block and enabling Multiple File Upload.
If you don’t know, to enable Multi Select in native input file element you have to use the attribute multiple
How to capture the progress of a File Upload
So, as the first topic, I will discuss here, how we can capture the upload progress of a file when it is being uploaded to a server.
If we break down this process in to simple steps as below,
- Need to send file from your PC/Client to your destination the Server
- To send those data, there should be a path between you/client and the server
- On your request, the communication channel is created between you and the server
- File information are sent part by part to the server
- Until the File is sent completely, this communication happens
- While the file data are sent, different message are being passed between the client and the server, we can group these messages in to some categories, we can call these categories as
HttpEventType
- So to capture these messages and what’s going on inside the above tunnel, we have to keep look at. Simply we need to observe/spy it.
To bring data from the Client to the Server, in Angular we have broker called HttpClient. This HttpClient has ability to spy above tunnel and to read all message. But we explicitly has to ask it to do.
In Angular, there is an option we can use observe
` in HttpClient request to spy a request.
this.httpClient.post(<Your URL>, <Body>,
{ headers: <Headers>, observe: 'events', // observe/spy the request })
So, as I explained above you know what observe does. But what is this events says then ?
By observe
` we ask HttpClient to spy on the request. It’s value says What exactly to spy on. There are 3 types we can ask it to spy on,
observe?: ‘body’ | ‘events’ | ‘response’
- body — Spy only on the body of the final response
- events — Spy on all the messages/HttpEventTypes happens in between
- response- Spy on the full response from the server
In brief, Angular support to capture following HttpEventTypes
/*** The request was sent out over the wire.*/Sent = 0,/*** An upload progress event was received.*/UploadProgress = 1,/*** The response status code and headers were received.*/ResponseHeader = 2,/*** A download progress event was received.*/DownloadProgress = 3,/*** The full response including the body was received.*/Response = 4,/*** A custom event from an interceptor or a backend.*/User = 5
So, now we have configure the man to spy on the request. But, we didn’t ask him to report us, what’s going on. Yes, now it is spy but doesn’t report back to us. So, another thing we have to do then, we have to ask it to report us.
To do that, we have to set another option reportProgress
`in Angular HttpClient
this.httpClient.post(<Your URL>, <Body>,
{ headers: <Headers>, observe: 'events', // observe/spy the request
reportProgress: true // Report the details to us
})
Okay then. Now we have completely asked HttpClient to spy the request and report it back to us, means we completed the flow How to capture the Progress of File Upload, not completed I didn’t show you to how to capture the progress value.
In my sample, I have fileUploadService.ts
` with the above code snippet.
In my component.ts, where I use the above service,
As we have requested event details, HttpClient returns event details as below.
{
"type": 1,
"loaded": 25690,
"total": 25690
}
- type — the HttpEventType
- loaded — How much uploaded by now
- total — total to be uploaded
I hope the rest you can understand. So, this how capture the progress of File Upload.
Next, there can be server side errors, when some misery happens in this journey from Client to Server, we must have to capture it and must need to reflect it to the end user, that something wrong happens. This is Capture File Upload Errors.
How to capture File Upload Errors
To capture error in our service, I do modify it as below. This is not somewhat to discuss in deep, as we use RxJS Observable in our service, so I use RxJS catchError operator.
return this.httpClient.post(toUploadFile.uploadUrl, toUploadFile.file,
{ headers: headers, observe: 'events', reportProgress: true
}).pipe(catchError((error: HttpErrorResponse) => throwError(error)));
Okay, now our service is strong to capture the errors if there are.
But, this doesn’t reflect to our component, as have not asked it. So we have to say it when we consume the service.
To do that, will modify our component where this file upload service is consumed, as below,
this.fileUploadService.uploadFiles(item).pipe( tap((event) => { if (event.type === HttpEventType.UploadProgress) { this.toUploadFilesList[index].uploadStatus.progressCount = Math.round(100 * (event.loaded / event.total)); }
}), catchError((error) => { <Do anything you want to do when error>
return of({ isError: true, index, error }); }));
So, here, where there s an error in the http request first it captures our service’s catchError
`, and then it throws to above catchError
` in the component.
Inside here, you can invoke what you want to do when there’s an http error. But here I return another Observable, as the response of our service is an Observable, so we must have to return Observable.
So, that’s all what you have to do capture event and not to break the Flow.
Simple theory, to not to break it, you have to handle it :P
Up to now, we have reached two goals,
- Capture the File Upload Progress
- Capture the File Upload Error
So, the next topic left is, How to Upload Multiple Files.
Upload Multiple Files, Capture the Progress and Error of each
So, with the above completed goals, now our application is cable to upload a file, and to capture its uploading progress and to capture the errors.
So, as an atomic file upload process, we have completed the flow. But we need to execute this for multiple files.
As an aspect, we can think, that’s easy, will execute the above steps for all the files within a loop, sequentially. Process in a queue.
Upload File 1 → Upload File 2 → Upload File 3 — — so on.
Technically we can afford this. But I see major drawbacks in this.
Do we have a concern like,
does any file upload should happen only if the previous upload is completed or does any file upload depend on the previous upload results ?
And next drawback is, in your files list there can be different files by size, type etc. So, the time to upload these files can be varied. So ,
why do you want to wait to upload a smaller file at the end, until a large file is uploaded ?
This would increase the time to process your files.
Answer for the above questions are obviously No. Then why do we need to upload these files by putting in a queue. We can execute each file upload independently. This sounds we can upload files parallelly.
So, technically, now we can understand that process these files within a loop is not the optimal solution. It’s a bullshit !
So, I create list of file upload requests against each file. But, now my problem is, how can I execute these requests parallelly, independently, only I want is the Upload Status and the Progress of each separately.
To process requests parallelly we have a man called forkJoin
` in RxJS. It says,
This operator is best used when you have a group of observables and only care about the final emitted value of each.
So, will use forkJoin
` to reach our goal.
first, I create list of Observable requests.
Second, will execute above list, using the forkJoin
`
That’s all. As the response of the forkJoin you will get responses of the each request as an array, as in the order of requests list you passed.
So, this is the end, so here we completed our application to be cable of handling multiple file uploads to not to break in the middle.
You can find the sample application here
If you enjoy the reading please clap, share and if you have a thought, question to ask, comment.
See you in another write up, until,
Reference