Search code examples
angularbase64progress

How to Detect When Form Change Event has Finished ( converting file to base64 )


I am uploading files to an API that expects a POST request with base64 information in order to "upload" files ( the API provider does not accept binary file upload). It works great for small to medium size files (under 2MB).

I am doing this in Angular by capturing the change event when a file is selected:

<input #file type="file" (change)="upload($event)" />

Then, in my upload() method, I am using the readAsDataURL() method to convert the $event data to base64. Finally, there is a button to press which sends a POST request to my API with the base64 data and credentials.

It works great when the file is small and the base64 encodes quickly, but when the file is large (around 4MB or more), the encoding takes a long time so the user can't tell if it was done being encoded. If they press the button to send the POST, it will fail if the encoding wasn't finished.

Is there a way to detect when the encoding is done or to somehow monitor the progress of the encoding?


Solution

  • It turns out that the FileReader constructor has an event called "onprogress" which can report the current progress. All I needed to do was attach the "onprogress" property to my instance of FileReader and I can get the progress with "lengthComputable" on the event (my example is using Angular 2 / 4):

    showProgress: number;
    
    let myReader: FileReader = new FileReader();
    
    myReader.onprogress = (e) => {
      if (e.lengthComputable) {
        //Round the progress to a percent whole number 
        this.showProgress = Math.round(100 * e.loaded / e.total);
        return this.showProgress;
      }
    }
    

    Then in order to bind this progress output to a Bootstrap progress bar, my html looks like this:

    <div class="progress">
               <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': showProgress + '%' }">
                   <span class="sr-only">{{ showProgress }}% Complete</span>
               </div>
       </div>