Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular - upload file as base64

I am trying to upload files from Angular 4 app to a JSON API service that accepts base64 strings as file content.

So what I do is - read the file with FileReader.readAsDataURL, then when user confirms the upload I will create a JSON request to the API and send the base64 string of the file I got earlier.

This is where the problem starts - as soon as I do something with the "content" (log it, send it, w/e) the request will be send, but its insanely slow, e.g. 20 seconds for 2MB file.

I have tried:

  • using ArrayBuffer and manually converting it to base64
  • storing the base64 string in HTML and retrieving it later
  • reading the files after user clicks on upload button
  • using the old client from @angular/common
  • using plain XHR request

but everything leads to the same result.

I know where the problem lies. But why does it happen? Is it something browser specific or angular specific? Is there a more preferred approach (keep in mind it has to be base64 string)?


Notes:

  • changing anything in the API is beyond my control
  • API is fine, sending any file trough postman will finish immediately

Code:

This method runs when user adds file to the dropzone:

public onFileChange(files: File[]) : void {
    files.forEach((file: File, index: number) => {
        const reader = new FileReader;

        // UploadedFile is just a simple model that contains filename, size, type and later base64 content
        this.uploadedFiles[index] = new UploadedFile(file);

        //region reader.onprogress
        reader.onprogress = (event: ProgressEvent) => {
            if (event.lengthComputable) {
                this.uploadedFiles[index].updateProgress(
                    Math.round((event.loaded * 100) / event.total)
                );
            }
        };
        //endregion

        //region reader.onloadend
        reader.onloadend = (event: ProgressEvent) => {
            const target: FileReader = <FileReader>event.target;
            const content = target.result.split(',')[1];

            this.uploadedFiles[index].contentLoaded(content);
        };
        //endregion

        reader.readAsDataURL(file);
    });
}

This method runs when users clicks save button

public upload(uploadedFiles: UploadedFile[]) : Observable<null> {
    const body: object = {
        files: uploadedFiles.map((uploadedFile) => {
            return {
                filename: uploadedFile.name,
                // SLOWDOWN HAPPENS HERE
                content: uploadedFile.content
            };
        })
    };

    return this.http.post('file', body)
}
like image 679
realshadow Avatar asked Dec 02 '17 00:12

realshadow


1 Answers

For sending big files to server you should use FormData to be able to send it as multi-part instead of a single big file.

Something like:

// import {Http, RequestOptions} from '@angular/http';
uploadFileToUrl(files, uploadUrl): Promise<any> {
  // Note that setting a content-type header
  // for mutlipart forms breaks some built in
  // request parsers like multer in express.
  const options = new RequestOptions();
  const formData = new FormData();

  // Append files to the virtual form.
  for (const file of files) {
    formData.append(file.name, file)
  }
  // Send it.
  return this.http.post(uploadUrl, formData, options);
    
}

Also don't forget to set the header 'Content-Type': undefined, I've scratched my head over this for hours.

like image 98
Yaser Avatar answered Nov 20 '22 15:11

Yaser