2

I have a setup for uploading files to AWS S3 via Angular 7 and Node.Js. The upload works fine. But there is an issue with the xhr.upload.onprogress event.

This event is only triggered when hosting the server via http. When using an https connection, the event isn't triggered once.

I tested it on heroku and my EC2 instance and came to the conclusion that only http works. Therefore, my production app hosted on AWS via https, doesn't work, either.

Here is the code (write a comment if more details are needed):

Angular Service (Frontend)

const xhr = new XMLHttpRequest();
let progress = 0;
xhr.upload.onprogress = (event: ProgressEvent) => {
    console.log('xhr on upload', event);
    if (event.lengthComputable) {
        progress = 100 * (event.loaded / event.total);
        console.log('file upload progress', { progress, file });
    }
};
xhr.responseType = 'json';
xhr.open('POST', `${API_URL}/${this.API_PATH}/upload`, true);
xhr.setRequestHeader('authorization', this.authService.getAuthToken());
xhr.send(payload);
xhr.onload = () => {
    observer.next(xhr.response);
    observer.complete();
    alive = false;
};

Node.Js

module.exports = async (req, res) => {
    try {
        const busboy = new Busboy({ headers: req.headers })
        busboy.on('finish', async () => {
            const fileData = req.files.file
            const fileId = req.body.fileId
            await uploadToAws(fileData)
            // ...
        })
        req.pipe(busboy)
    } catch (err) { /* ... */ }
}
const uploadToAws = async (fileData, file) => {
    return new Promise((resolve, reject) => {
        const params = {
            Body: fileData.data,
            Bucket: awsConfig.buckets.drakery,
            ContentType: fileData.mimetype,
            Key: `mykey`,
            StorageClass: 'ONEZONE_IA',
        }
        awsConfig.s3
        .upload(params, (err, data) => err ? reject(err) : resolve(data.Location))
        .on('httpUploadProgress', function (evt) {
            console.log('httpUploadProgress', evt);
        })
})

I am really confused why this is happening. I couldn't find any resources online on this problem.

I am really thankful for every help!

Thanks

Florian Ludewig
  • 4,338
  • 11
  • 71
  • 137
  • What does `xhr.onload` do? Try removing it, see if it works. You could also add a `content-type` header to the request. – lofihelsinki Mar 08 '19 at 14:05
  • `xhr.onload` is called when the request finished... I added a `content-type` and removed `xhr.onload` but it still doesn't work with a https connection – Florian Ludewig Mar 08 '19 at 15:14
  • Does the file get to your Node.js server or does it not even get that far when served over HTTPs? Are you sure you aren't accidentally making a cross origin request by doing something like posting to HTTP:// instead of HTTPS:// when serving over HTTPS? What does the value for your host equal when it works and what does it equal when it does not?(I am talking about `${API_URL}/${this.API_PATH}/upload`) – Jonathan Mar 08 '19 at 18:46
  • The file reaches the server fine (also via https) --- I don't make a cross origin request ---- API_URL = `${window.location.protocol}//${window.location.hostname}:3000/api – Florian Ludewig Mar 08 '19 at 19:42
  • What is the URL you are trying to use? Have you tried explicitly adding `https` as part of the URL you are trying to access? – Pytth Mar 08 '19 at 19:59
  • When hosting the website on on `https` the url looks like `https://example.com/api/...` and on `http` it looks like `http://example.com/api/...` So that should be fine – Florian Ludewig Mar 08 '19 at 20:03
  • I'm having the same issue - but I don't know if it's https related. I'm also trying to get it work on EC2 over AWS. – friek108 May 28 '20 at 06:43
  • 1
    @friek108 Really strange. Maybe you can checkout this question: https://stackoverflow.com/questions/55782997 – Florian Ludewig May 28 '20 at 06:56

1 Answers1

1

this is my basic js code that I have used for a web project:

        ...
        var xhr = new XMLHttpRequest();

        xhr.upload.addEventListener("progress", uploadProgress, false);

        xhr.addEventListener("load", uploadComplete, false);

        xhr.addEventListener("error", uploadFailed, false);

        xhr.addEventListener("abort", uploadCanceled, false);

        xhr.open("POST", "/controller");
        // fd : formData
        xhr.send(fd);

    }

    function uploadProgress(evt) {

        if (evt.lengthComputable) {

            var percentComplete = Math.round(evt.loaded * 100 / evt.total);
            ...
        }

        else {

            ...
        }

    }

As you can see I'm adding an event listener on 'progress', I'm not overwriting the other callback function like you do with (=) ( maybe https uses another callback and by overwriting the onprogress like you do the callback does not work properly ):

xhr.upload.onprogress = (event: ProgressEvent) => {
    console.log('xhr on upload', event);
    if (event.lengthComputable) {
        progress = 100 * (event.loaded / event.total);
        console.log('file upload progress', { progress, file });
    }
};

Try this out, hope it helps you, have a nice day

RLoris
  • 526
  • 5
  • 14