Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ThePenguin's avatar

Update laravel-vapor NPM / JS script to use XmlHttpRequest

Out of the box, Laravel Vapor includes an NPM script for streaming uploads to S3 directly.

The docs for this are available here: https://docs.vapor.build/1.0/resources/storage.html#file-uploads

While the script itself is available here: https://github.com/laravel/vapor-js/blob/master/src/index.js

The script itself is minimal, ~40 lines of code, however it requires axios as a dependency, which is quite heavy for such a tiny script. I've been taking steps to remove Axios since the native fetch API now has good browser support.

Unfortunately, fetch doesn't currently support providing upload progress. The general consensus seems to be that you should use a standard XmlHttpRequest for that functionality.

I'm wondering if there is value in rewriting the script to eliminate the need for the dependency? Thoughts @themsaid?

Thanks!

0 likes
3 replies
ThePenguin's avatar

Something like this for example...

let body = new FormData();
body.append('bucket', options.bucket || '');
body.append('content_type', options.contentType || file.type);
body.append('expires', options.expires || '');
body.append('visibility', options.visibility || '');

let payload = {
    method      : 'post',
    credentials : 'same-origin',
    body        : body,
    headers     : {
        'X-Requested-With' : 'XMLHttpRequest',
    },
};

let response = await fetch('/vapor/signed-storage-url', payload);
let data = await response.json();

let headers = data.headers;

if ('Host' in headers) {
    delete headers.Host;
}

data.extension = file.name.split('.').pop();

await function() {
    return new Promise(function (resolve, reject) {

        let xhr = new XMLHttpRequest();
        xhr.open('PUT', data.url);

        xhr.onload = () => resolve(xhr.response);
        xhr.upload.onprogress = (e) => options.progress(Math.ceil((e.loaded / e.total) * 100));

        for (const header in headers) {
            xhr.setRequestHeader(header, headers[header]);
        }

        xhr.send(file);

    });
}();

return data;

I used fetch for the first request since there's no need to monitor upload progress and fetch doesn't require additional logic to work with promises unlike XHR.

themsaid's avatar

You can submit this as a Pull Request so we can review.

Please or to participate in this conversation.