Open Salma7amed opened 7 years ago
multi fileupload by using flowJs multiple request fine depends on fileFize. can any one ng2-file-upload problem on multiplefile.
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?
@rbasniak not using this library.
@Salma7amed What library are you using now?
@rbasniak I used FormData to perform a post request with the files.
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.
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.
@josecarlosaparicio : uploadAllFiles(){ this.uploader.uploadAll(); } You mean this.uploader.uploadAllFiles(); ?
@josecarlosaparicio : onSuccess does not read the response if server is going to return a json object. Any way to fix this?
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)?
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.
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" :)
@judsonmusic I'm currently working on this feature, I will open up a PR as soon as it's finished :)
We're trying to get this into a better shape so if you still have that PR it would be awesome 😉
If no one gets to this I can try and throw something together if more details are provided.
@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.
@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);
}
}
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.
@adrianfaciu Is anyone working on this? I could do a PR otherwise
As far as I know, no, there is no open PR for this.
@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.
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.
@eikishi01 you could use both. The way how multiple upload is done is based on configuration of the upload component.
@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.
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
@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
);
}
};
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.
@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?
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 ?