valor-software / ng2-file-upload

Easy to use Angular components for files upload
http://valor-software.github.io/ng2-file-upload/
MIT License
1.91k stars 662 forks source link

onCompleteItem seems to be getting called before the file is done uploading #1001

Open b0rgbart3 opened 6 years ago

b0rgbart3 commented 6 years ago

Uploading an image is working - but sometimes my onCompleteItem() method seems to get called before the file is done uploading. This causes me to (sometimes) get a broken image link in my template. My API is using Multer-S3 to upload the image to AWS - so perhaps the issue is on that end? Any comments would be appreciated. Thanks!

        this.imageUploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
            this.avatarChanged = true;
            this.imageUrl = null;
                        this.tempName = this.imageUploader.queue[0].file.name;
                         this.image = this.tempName;
                         this.imageUrl = this.globals.avatars + '/' + this.user.id + '/' + this.image;
                         this.imageUploader.queue[0].remove();
                         this.updateDisplay();
                     };
koenvanderlinden commented 6 years ago

Are you sure that when AWS accepted upload, the file is immediately available on the url? Maybe there is a small delay in AWS to make it available. If it's on a CDN the delay can be even longer.

Maybe you can set the imageUrl with a small delay? The code suggest that the onCompleteItem is only called after the connection received an error, abort or onLoad event from the XMLHttpRequest connection.

b0rgbart3 commented 6 years ago

Thank you - koenvanderlinden - for your response.

The good news is that it's now working. The bad news is that I can't really say what I did to fix it.
It may have been partially due to the fact that I had an issue on the back-end - where my response object didn't have the right CORS headers - so the image was uploading fine -but there was a 'premature' response that came back with a CORS warning message - which may have triggered the OnCompleteItem method.

However - upon further reading I did also learn that you are right -- when uploading to AWS - there can be a slight delay between the time the file is finished uploading and the time that it is publicly accessible --- which also may have been happening. So YES - I think a small delay is probably a good idea. -- Another idea I had was to do a Get Request of the image through the API to make sure it's available -- before assigning it to a local variable that triggers the template to display the image.

b0rgbart3 commented 6 years ago

I just found this little method in the AWS SDK ( S3 ) - which looks like it could handle this for us:


var params = {
  Bucket: 'STRING_VALUE', /* required */
  Key: 'STRING_VALUE', /* required */
};
s3.waitFor('objectExists', params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});
alex-ponce commented 6 years ago

I'm having a similar issue (onCompleteItem called prematurely) only my backend is not S3, I'm calling my own Spring Boot service which returns 200 only upon success. Is it possible for onCompleteItem to be called in other conditions? @koenvanderlinden do you mind expanding on this comment: "The code suggest that the onCompleteItem is only called after the connection received an error, abort or onLoad event from the XMLHttpRequest connection."

koenvanderlinden commented 6 years ago

@alex-ponce This ng2-file-upload control (file-uploader.class.ts) only calls onCompleteItem when there is a error, an abort or upload has finished event. Check out this code snippet from the file-uploader.class.ts.

let xhr = item._xhr = new XMLHttpRequest();
...
...
xhr.onload = () => { // when uploading is done
      let headers = this._parseHeaders(xhr.getAllResponseHeaders());
      let response = this._transformResponse(xhr.response, headers);
      let gist = this._isSuccessCode(xhr.status) ? 'Success' : 'Error';
      let method = '_on' + gist + 'Item';
      (this as any)[ method ](item, response, xhr.status, headers);
      this._onCompleteItem(item, response, xhr.status, headers);
    };
    xhr.onerror = () => { // in case of error
      let headers = this._parseHeaders(xhr.getAllResponseHeaders());
      let response = this._transformResponse(xhr.response, headers);
      this._onErrorItem(item, response, xhr.status, headers);
      this._onCompleteItem(item, response, xhr.status, headers);
    };
    xhr.onabort = () => {// in case of aborting the upload
      let headers = this._parseHeaders(xhr.getAllResponseHeaders());
      let response = this._transformResponse(xhr.response, headers);
      this._onCancelItem(item, response, xhr.status, headers);
      this._onCompleteItem(item, response, xhr.status, headers);
    };