Upload multiple files in one request #671

Open
opened 2017-03-14 13:47:53 +00:00 by Salma7amed · 28 comments
Salma7amed commented 2017-03-14 13:47:53 +00:00 (Migrated from github.com)

What I noticed is that if I upload multiple files at once. The uploader performs multiple requests to the url for each single file. So is it possible to receive all the files in one request ?

What I noticed is that if I upload multiple files at once. The uploader performs multiple requests to the url for each single file. So is it possible to receive all the files in one request ?
vijj commented 2017-03-24 09:18:48 +00:00 (Migrated from github.com)

multi fileupload by using flowJs multiple request fine depends on fileFize. can any one ng2-file-upload problem on multiplefile.

multi fileupload by using flowJs multiple request fine depends on fileFize. can any one ng2-file-upload problem on multiplefile.
rbasniak commented 2017-04-02 00:00:41 +00:00 (Migrated from github.com)

I noticed this too, the .uploadAll() method does a single request for every file in queue.

@Salma7amed Did have any sucess on uploading all files in a single request?

I noticed this too, the `.uploadAll()` method does a single request for every file in queue. @Salma7amed Did have any sucess on uploading all files in a single request?
Salma7amed commented 2017-04-05 12:31:01 +00:00 (Migrated from github.com)

@rbasniak not using this library.

@rbasniak not using this library.
rbasniak commented 2017-04-05 12:34:03 +00:00 (Migrated from github.com)

@Salma7amed What library are you using now?

@Salma7amed What library are you using now?
Salma7amed commented 2017-04-05 19:34:07 +00:00 (Migrated from github.com)

@rbasniak I used FormData to perform a post request with the files.

@rbasniak I used FormData to perform a post request with the files.
ClaytonBrawley commented 2017-04-06 17:57:13 +00:00 (Migrated from github.com)

The file that uploads the attached files in ngx-uploader.ts has a function that it uses called uploadFilesInQueue(). What this function does is loop through the queue and sends each file off to uploadFile() to be sent off using a XMLHttpRequest. What you can do to send all files at one time is, instead of calling uploadFile() for each file in the queue, add each file to the FormData form before sending it off. Something like this:

uploadFilesInQueue(): void {
    if (this.getQueueSize() === 1) {
      this.uploadFile(this._queue[0]);
    } else if (this.getQueueSize() > 1) {
      this.uploadAllFiles();
    }
  }

uploadAllFiles(): void {
    const xhr = new XMLHttpRequest();
    const form = new FormData();

    for (const file of this._queue) {
      form.append(this.opts.fieldName, file, file.name);
    }

    xhr.send(form);
  };

Just like @Salma7amed said above.

The file that uploads the attached files in `ngx-uploader.ts` has a function that it uses called `uploadFilesInQueue()`. What this function does is loop through the queue and sends each file off to `uploadFile()` to be sent off using a `XMLHttpRequest`. What you can do to send all files at one time is, instead of calling `uploadFile()` for each file in the queue, add each file to the `FormData` form before sending it off. Something like this: ``` uploadFilesInQueue(): void { if (this.getQueueSize() === 1) { this.uploadFile(this._queue[0]); } else if (this.getQueueSize() > 1) { this.uploadAllFiles(); } } uploadAllFiles(): void { const xhr = new XMLHttpRequest(); const form = new FormData(); for (const file of this._queue) { form.append(this.opts.fieldName, file, file.name); } xhr.send(form); }; ``` Just like @Salma7amed said above.
josecarlosaparicio commented 2017-07-06 12:50:48 +00:00 (Migrated from github.com)

Finally, I was inspired thanks to @ClaytonBrawley and can solve this issue extending FileUploader class:

import { FileUploader, FileItem, FileUploaderOptions } from 'ng2-file-upload';

export class FileUploaderCustom extends FileUploader {

  constructor(options: FileUploaderOptions) {
    super(options);
  }

  uploadAllFiles(): void {

    var xhr = new XMLHttpRequest();
    var sendable = new FormData();
    var fakeitem: FileItem = null;
    this.onBuildItemForm(fakeitem, sendable);

    for (const item of this.queue) {
      item.isReady = true;
      item.isUploading = true;
      item.isUploaded = false;
      item.isSuccess = false;
      item.isCancel = false;
      item.isError = false;
      item.progress = 0;

      if (typeof item._file.size !== 'number') {
        throw new TypeError('The file specified is no longer valid');
      }
      sendable.append("files", item._file, item.file.name);
    }

    if (this.options.additionalParameter !== undefined) {
      Object.keys(this.options.additionalParameter).forEach((key) => {
        sendable.append(key, this.options.additionalParameter[key]);
      });
    }

    xhr.onload = () => {
      var gist = (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 ? 'Success' : 'Error';
      var method = 'on' + gist + 'Item';
      this[method](fakeitem, null, xhr.status, null);

    };
    xhr.onerror = () => {
      this.onErrorItem(fakeitem, null, xhr.status, null);
    };

    xhr.onabort = () => {
      this.onErrorItem(fakeitem, null, xhr.status, null);
    };

    xhr.open("POST", this.options.url, true);
    xhr.withCredentials = true;
    if (this.options.headers) {
      for (var _i = 0, _a = this.options.headers; _i < _a.length; _i++) {
        var header = _a[_i];
        xhr.setRequestHeader(header.name, header.value);
      }
    }
    if (this.authToken) {
      xhr.setRequestHeader(this.authTokenHeader, this.authToken);
    }
    xhr.send(sendable);
  };

}

Then, in the component can be use like this:

uploader: FileUploaderCustom;

ngOnInit() {
    this.uploader = new FileUploaderCustom ({
        url: urlSubirMiniatura
    });
}

uploadAllFiles(){
   this.uploader.uploadAll();
}

Notice that all files are appending to sendable with the name "files", so it could be refactored better.

Finally, I was inspired thanks to @ClaytonBrawley and can solve this issue extending FileUploader class: ``` import { FileUploader, FileItem, FileUploaderOptions } from 'ng2-file-upload'; export class FileUploaderCustom extends FileUploader { constructor(options: FileUploaderOptions) { super(options); } uploadAllFiles(): void { var xhr = new XMLHttpRequest(); var sendable = new FormData(); var fakeitem: FileItem = null; this.onBuildItemForm(fakeitem, sendable); for (const item of this.queue) { item.isReady = true; item.isUploading = true; item.isUploaded = false; item.isSuccess = false; item.isCancel = false; item.isError = false; item.progress = 0; if (typeof item._file.size !== 'number') { throw new TypeError('The file specified is no longer valid'); } sendable.append("files", item._file, item.file.name); } if (this.options.additionalParameter !== undefined) { Object.keys(this.options.additionalParameter).forEach((key) => { sendable.append(key, this.options.additionalParameter[key]); }); } xhr.onload = () => { var gist = (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 ? 'Success' : 'Error'; var method = 'on' + gist + 'Item'; this[method](fakeitem, null, xhr.status, null); }; xhr.onerror = () => { this.onErrorItem(fakeitem, null, xhr.status, null); }; xhr.onabort = () => { this.onErrorItem(fakeitem, null, xhr.status, null); }; xhr.open("POST", this.options.url, true); xhr.withCredentials = true; if (this.options.headers) { for (var _i = 0, _a = this.options.headers; _i < _a.length; _i++) { var header = _a[_i]; xhr.setRequestHeader(header.name, header.value); } } if (this.authToken) { xhr.setRequestHeader(this.authTokenHeader, this.authToken); } xhr.send(sendable); }; } ``` Then, in the component can be use like this: ``` uploader: FileUploaderCustom; ngOnInit() { this.uploader = new FileUploaderCustom ({ url: urlSubirMiniatura }); } uploadAllFiles(){ this.uploader.uploadAll(); } ``` Notice that all files are appending to sendable with the name "files", so it could be refactored better.
varrob112 commented 2017-07-16 07:20:40 +00:00 (Migrated from github.com)

@josecarlosaparicio :
uploadAllFiles(){
this.uploader.uploadAll();
}
You mean this.uploader.uploadAllFiles(); ?

@josecarlosaparicio : uploadAllFiles(){ this.uploader.uploadAll(); } You mean this.uploader.uploadAllFiles(); ?
dannyhchan commented 2017-08-10 16:35:24 +00:00 (Migrated from github.com)

@josecarlosaparicio :
onSuccess does not read the response if server is going to return a json object. Any way to fix this?

@josecarlosaparicio : onSuccess does not read the response if server is going to return a json object. Any way to fix this?
Max053 commented 2017-08-15 11:59:24 +00:00 (Migrated from github.com)

Is this 1 file per request the expected behaviour from the beginning, or would a PR be accepted if it were to fix this behaviour (maybe with an optional setter)?

Is this 1 file per request the expected behaviour from the beginning, or would a PR be accepted if it were to fix this behaviour (maybe with an optional setter)?
dannyhchan commented 2017-08-15 12:16:45 +00:00 (Migrated from github.com)

1 file per request is working as expected. I think with an option setter for upload all rather than per file would be good. What I have as a workaround is all on the server end by submitting the total queue count and along with the files and handling it with the 1 file per upload scenario. If the entire batch of files can be submitted at one, this could be avoided.

1 file per request is working as expected. I think with an option setter for upload all rather than per file would be good. What I have as a workaround is all on the server end by submitting the total queue count and along with the files and handling it with the 1 file per upload scenario. If the entire batch of files can be submitted at one, this could be avoided.
judsonmusic commented 2017-08-18 04:05:59 +00:00 (Migrated from github.com)

Big TIME +1
I love the plugin but its steers away from server performance by doing multiple requests. I would love to be able to send all of the file in an array at once and loop through the file array
files.map(obj)=>{ fs.writeFIle... }

I am not a big fan of extending(hacking) other people's work, "Patience is a Virtue" :)

Big TIME +1 I love the plugin but its steers away from server performance by doing multiple requests. I would love to be able to send all of the file in an array at once and loop through the file array `files.map(obj)=>{ fs.writeFIle... }` I am not a big fan of extending(hacking) other people's work, "Patience is a Virtue" :)
Max053 commented 2017-08-20 15:46:42 +00:00 (Migrated from github.com)

@judsonmusic I'm currently working on this feature, I will open up a PR as soon as it's finished :)

@judsonmusic I'm currently working on this feature, I will open up a PR as soon as it's finished :)
adrianfaciu commented 2017-10-02 15:32:48 +00:00 (Migrated from github.com)

We're trying to get this into a better shape so if you still have that PR it would be awesome 😉

We're trying to get this into a better shape so if you still have that PR it would be awesome 😉
ClaytonBrawley commented 2017-10-02 16:55:01 +00:00 (Migrated from github.com)

If no one gets to this I can try and throw something together if more details are provided.

If no one gets to this I can try and throw something together if more details are provided.
adrianfaciu commented 2017-10-02 19:32:27 +00:00 (Migrated from github.com)

@ClaytonBrawley you can assign this to yourself and see what you can done. Once you have something create a PR and we can all have a look.

@ClaytonBrawley you can assign this to yourself and see what you can done. Once you have something create a PR and we can all have a look.
andrew-starosciak commented 2017-10-13 17:45:20 +00:00 (Migrated from github.com)

@dannyhchan See below for a workaround on firing the onCompleteItem callback.

import {FileItem, FileUploader, FileUploaderOptions} from 'ng2-file-upload';

export class FileUploaderCustom extends FileUploader {

    constructor(
        options: FileUploaderOptions
    ) {
        super(options);
    }

    uploadAllFiles(): void {
        // const _this = this;
        const xhr = new XMLHttpRequest();
        const sendable = new FormData();
        const fakeItem: FileItem = null;
        this.onBuildItemForm(fakeItem, sendable);

        for (const item of this.queue) {
            item.isReady = true;
            item.isUploading = true;
            item.isUploaded = false;
            item.isSuccess = false;
            item.isCancel = false;
            item.isError = false;
            item.progress = 0;

            if (typeof item._file.size !== 'number') {
                throw new TypeError('The file specified is no longer valid');
            }
            sendable.append('files[]', item._file, item.file.name);
        }

        if (this.options.additionalParameter !== undefined) {
            Object.keys(this.options.additionalParameter).forEach((key) => {
                sendable.append(key, this.options.additionalParameter[key]);
            })
        }

        xhr.onerror = () => {
            this.onErrorItem(fakeItem, null, xhr.status, null);
        }

        xhr.onabort = () => {
            this.onErrorItem(fakeItem, null, xhr.status, null);
        }

        xhr.open('POST', this.options.url, true);
        xhr.withCredentials = true;
        if (this.options.headers) {
            for (let _i = 0, _a = this.options.headers; _i < _a.length; _i++) {
                const header = _a[_i];
                xhr.setRequestHeader(header.name, header.value);
            }
        }
        if (this.authToken) {
            xhr.setRequestHeader(this.authTokenHeader, this.authToken);
        }

        xhr.onload = () => {
            const headers = this._parseHeaders(xhr.getAllResponseHeaders());
            const response = this._transformResponse(xhr.response, headers);
            const gist = this._isSuccessCode(xhr.status) ? 'Success' : 'Error';
            const method = '_on' + gist + 'Item';
            for (const item of this.queue) {
                this[method](item, response, xhr.status, headers);
            }
            this._onCompleteItem(this.queue[0], response, xhr.status, headers);
        }

        xhr.send(sendable);
    }
}
@dannyhchan See below for a workaround on firing the onCompleteItem callback. ``` import {FileItem, FileUploader, FileUploaderOptions} from 'ng2-file-upload'; export class FileUploaderCustom extends FileUploader { constructor( options: FileUploaderOptions ) { super(options); } uploadAllFiles(): void { // const _this = this; const xhr = new XMLHttpRequest(); const sendable = new FormData(); const fakeItem: FileItem = null; this.onBuildItemForm(fakeItem, sendable); for (const item of this.queue) { item.isReady = true; item.isUploading = true; item.isUploaded = false; item.isSuccess = false; item.isCancel = false; item.isError = false; item.progress = 0; if (typeof item._file.size !== 'number') { throw new TypeError('The file specified is no longer valid'); } sendable.append('files[]', item._file, item.file.name); } if (this.options.additionalParameter !== undefined) { Object.keys(this.options.additionalParameter).forEach((key) => { sendable.append(key, this.options.additionalParameter[key]); }) } xhr.onerror = () => { this.onErrorItem(fakeItem, null, xhr.status, null); } xhr.onabort = () => { this.onErrorItem(fakeItem, null, xhr.status, null); } xhr.open('POST', this.options.url, true); xhr.withCredentials = true; if (this.options.headers) { for (let _i = 0, _a = this.options.headers; _i < _a.length; _i++) { const header = _a[_i]; xhr.setRequestHeader(header.name, header.value); } } if (this.authToken) { xhr.setRequestHeader(this.authTokenHeader, this.authToken); } xhr.onload = () => { const headers = this._parseHeaders(xhr.getAllResponseHeaders()); const response = this._transformResponse(xhr.response, headers); const gist = this._isSuccessCode(xhr.status) ? 'Success' : 'Error'; const method = '_on' + gist + 'Item'; for (const item of this.queue) { this[method](item, response, xhr.status, headers); } this._onCompleteItem(this.queue[0], response, xhr.status, headers); } xhr.send(sendable); } } ```
drdreo commented 2017-11-25 14:17:52 +00:00 (Migrated from github.com)

Ran into the same Issue. Ended up rewriting my process function on the server to handle one item per call and not all at once. Annoying because i can't bulk further requests.

Ran into the same Issue. Ended up rewriting my process function on the server to handle one item per call and not all at once. Annoying because i can't bulk further requests.
a-morn commented 2018-01-24 12:02:56 +00:00 (Migrated from github.com)

@adrianfaciu Is anyone working on this? I could do a PR otherwise

@adrianfaciu Is anyone working on this? I could do a PR otherwise
adrianfaciu commented 2018-01-24 17:39:02 +00:00 (Migrated from github.com)

As far as I know, no, there is no open PR for this.

As far as I know, no, there is no open PR for this.
koenvanderlinden commented 2018-03-17 16:10:37 +00:00 (Migrated from github.com)

@adrianfaciu @a-morn I create a PR #993 for a multiupload in one reqeust. It probably needs some refactoring. Let me know what you think of it.

@adrianfaciu @a-morn I create a PR #993 for a multiupload in one reqeust. It probably needs some refactoring. Let me know what you think of it.
eikishi01 commented 2019-02-11 14:04:30 +00:00 (Migrated from github.com)

I was just looking for a way to upload all files individually and I got to this threat finding out that it is the default behaviour, which is what I needed @koenvanderlinden does you PR #993 consider allowing the use of both approaches? both use cases are valid and it should count for them.

I was just looking for a way to upload all files individually and I got to this threat finding out that it is the default behaviour, which is what I needed @koenvanderlinden does you PR #993 consider allowing the use of both approaches? both use cases are valid and it should count for them.
koenvanderlinden commented 2019-02-12 18:44:13 +00:00 (Migrated from github.com)

@eikishi01 you could use both. The way how multiple upload is done is based on configuration of the upload component.

@eikishi01 you could use both. The way how multiple upload is done is based on configuration of the upload component.
TianrenWang commented 2019-07-15 14:44:03 +00:00 (Migrated from github.com)

@andrew-starosciak
Can you explain the reasoning behind doing a for loop on each item in the queue? With the for loop you receive multiple responses from the uploaded server, when only one response is necessary.

@andrew-starosciak Can you explain the reasoning behind doing a for loop on each item in the queue? With the for loop you receive multiple responses from the uploaded server, when only one response is necessary.
sridharan31 commented 2019-11-14 14:30:55 +00:00 (Migrated from github.com)

this.uploader.clearQueue(); onsucess file not removed get error

mandateCancelComponent.html:786 ERROR TypeError: Cannot read property 'abort' of undefined
    at FileUploaderCustom.push../node_modules/ng2-file-upload/file-upload/file-uploader.class.js.FileUploader.cancelItem (file-uploader.class.js:112)
    at FileItem.push../node_modules/ng2-file-upload/file-upload/file-item.class.js.FileItem.cancel (file-item.class.js:38)
    at FileUploaderCustom.push../node_modules/ng2-file-upload/file-upload/file-uploader.class.js.FileUploader.removeFromQueue (file-uploader.class.js:85)
    at FileItem.push../node_modules/ng2-file-upload/file-upload/file-item.class.js.FileItem.remove (file-item.class.js:41)
    at Object.eval [as handleEvent] (mandateCancelComponent.html:795)
    at handleEvent (core.js:28969)
    at callWithDebugContext (core.js:30039)
    at Object.debugHandleEvent [as handleEvent] (core.js:29766)
    at dispatchEvent (core.js:19631)
    at core.js:28178
this.uploader.clearQueue(); onsucess file not removed get error ``` mandateCancelComponent.html:786 ERROR TypeError: Cannot read property 'abort' of undefined at FileUploaderCustom.push../node_modules/ng2-file-upload/file-upload/file-uploader.class.js.FileUploader.cancelItem (file-uploader.class.js:112) at FileItem.push../node_modules/ng2-file-upload/file-upload/file-item.class.js.FileItem.cancel (file-item.class.js:38) at FileUploaderCustom.push../node_modules/ng2-file-upload/file-upload/file-uploader.class.js.FileUploader.removeFromQueue (file-uploader.class.js:85) at FileItem.push../node_modules/ng2-file-upload/file-upload/file-item.class.js.FileItem.remove (file-item.class.js:41) at Object.eval [as handleEvent] (mandateCancelComponent.html:795) at handleEvent (core.js:28969) at callWithDebugContext (core.js:30039) at Object.debugHandleEvent [as handleEvent] (core.js:29766) at dispatchEvent (core.js:19631) at core.js:28178 ```
ghost commented 2020-03-30 12:57:28 +00:00 (Migrated from github.com)

@andrew-starosciak

i think this works for multiple files :

xhr.onload = () => {
      const headers = this._parseHeaders(xhr.getAllResponseHeaders());
      const response = this._transformResponse(xhr.response, headers);
      const gist = this._isSuccessCode(xhr.status) ? "Success" : "Error";
      const method = "_on" + gist + "Item";
      const queueLength = this.queue.length;
      for (var i = 0; i < queueLength; i++) {
        this[method](
          this.queue[this.queue.length - 1],
          response,
          xhr.status,
          headers
        );
        this._onCompleteItem(
          this.queue[this.queue.length - 1],
          response,
          xhr.status,
          headers
       );
     }   
 };
> @andrew-starosciak i think this works for multiple files : ``` xhr.onload = () => { const headers = this._parseHeaders(xhr.getAllResponseHeaders()); const response = this._transformResponse(xhr.response, headers); const gist = this._isSuccessCode(xhr.status) ? "Success" : "Error"; const method = "_on" + gist + "Item"; const queueLength = this.queue.length; for (var i = 0; i < queueLength; i++) { this[method]( this.queue[this.queue.length - 1], response, xhr.status, headers ); this._onCompleteItem( this.queue[this.queue.length - 1], response, xhr.status, headers ); } }; ```
costeacosmin92 commented 2020-04-16 16:56:10 +00:00 (Migrated from github.com)

Hi everyone,
I am using this plugin with Angular 9, and Spring Boot on backend, and was asked to create a questionnaire where for every item on the checklist the user can upload one or more pictures. My trouble was the same as the OP's, and ended up getting FileItem[] array from the 'uploader', and putting each file on a Zip file using JSZip library.

import { FileUploader, FileUploaderOptions, FileItem } from 'ng2-file-upload';
import * as JSZip from 'jszip';

async onFormSubmit() {
      let zipFile: JSZip = new JSZip();

      let items: FileItem[] = this.uploader.getNotUploadedItems().filter((item: FileItem) => 
           !item.isUploading);

      items.forEach(item  =>{
          zipFile.file(item.file.name, item.file.rawFile, {base64: true});
      })

      let finalZip = await zipFile.generateAsync({type:"blob", compression: "DEFLATE"});

     // now you can send 'finalZip' to backend using common HttpClient .
}

Hope it can be useful.

Hi everyone, I am using this plugin with Angular 9, and Spring Boot on backend, and was asked to create a questionnaire where for every item on the checklist the user can upload one or more pictures. My trouble was the same as the OP's, and ended up getting FileItem[] array from the 'uploader', and putting each file on a Zip file using JSZip library. import { FileUploader, FileUploaderOptions, FileItem } from 'ng2-file-upload'; import * as JSZip from 'jszip'; async onFormSubmit() { let zipFile: JSZip = new JSZip(); let items: FileItem[] = this.uploader.getNotUploadedItems().filter((item: FileItem) => !item.isUploading); items.forEach(item =>{ zipFile.file(item.file.name, item.file.rawFile, {base64: true}); }) let finalZip = await zipFile.generateAsync({type:"blob", compression: "DEFLATE"}); // now you can send 'finalZip' to backend using common HttpClient . } Hope it can be useful.
SohrabRo commented 2021-12-18 10:17:30 +00:00 (Migrated from github.com)

@josecarlosaparicio your solution works like a charm but the progress bar is set to 0 and it is never updated! Therefore the progress bar does not work. Any solution about this problem you may think of?

@josecarlosaparicio your solution works like a charm but the progress bar is set to 0 and it is never updated! Therefore the progress bar does not work. Any solution about this problem you may think of?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dc/ng2-file-upload#671