Uploading files using AJAX directly to S3

Chris White, Laravel Developer:

For most situations using S3 is a no brainer, but the majority of developers transfer their user’s uploads to S3 after they have received them on the server side. This doesn’t have to be the case, your user’s web browser can send the file directly to an S3 bucket. You don’t even have to open the bucket up to the public. Signed upload URLs with an expiry will allow temporary access to upload a single object.

This is exactly what me and my colleagues at Small Town Heroes did for The Passion: #ikloopmee. When the user has created his own testimonial (which is rendered onto a <canvas> element) he directly uploads the blob from his browser to S3 using a signed URL.

Here’s what our upload code looks like (given a already signed URL):

// Upload the blob to S3
$.ajax({
    method: 'PUT',
    contentType: s3Data.contentType,
    headers: {
        'Cache-Control': 'max-age=86400000',
        'x-amz',
    },
    processData: false,
    url: s3Data.signedUrl,
    data: blob,
}).done((data) =>
    // Hurray!
).fail((err) =>
    // Oops!
);

In order to get this to work, we did have to extend jQuery with a binary transport (which is a slightly adjusted version of this original):

/**
 *
 * jquery.binarytransport.js
 *
 * @description. jQuery ajax transport for making binary data type requests.
 * @version 1.1
 * @author Henry Algus <henryalgus@gmail.com>
 * @author Bramus <bramus@bram.us>
 *
 */

// use this transport for "binary" data type
$.ajaxTransport("+binary", function(options, originalOptions, jqXHR){
    // check for conditions and support for blob / arraybuffer response type
    if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob)))))
    {
        return {
            // create new XMLHttpRequest
            send: function(headers, callback){
                // setup all variables
                var xhr = new XMLHttpRequest(),
                url = options.url,
                type = options.type,
                async = options.async || true,
                // blob or arraybuffer. Default is blob
                dataType = options.responseType || "blob",
                data = options.data || null,
                username = options.username || null,
                password = options.password || null;

                xhr.addEventListener('load', function(){
                        var data = {};
                        data[options.dataType] = xhr.response;
                        // make callback and send data
                        callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
                });

                xhr.open(type, url, async, username, password);

                // setup custom headers
                for (var i in headers ) {
                        xhr.setRequestHeader(i, headers[i] );
                }

                xhr.responseType = dataType;
                xhr.send(data);
            },
            abort: function(){
                return false;
            }
        };
    }
});

Avoiding the burden of file uploads →

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

Ajax is “broken” in iOS6

In iOS6, POST requests over XHR seem to be cached quite aggressively. Luckily, it’s fixable:

After a bit of investigation, turns out that Safari on iOS6 will cache POSTs that have either no Cache-Control headers or even Cache-Control: max-age=0.

The only way I’ve found of preventing this caching from happening at a global level rather than having to hack random querystrings onto the end of service calls is to set Cache-Control: no-cache.

Don’t break the web, right?

Is Safari on iOS 6 caching $.ajax results? →
Understanding the iOS6 AJAX bugs →

jQuery.serializeAnything : Serialize anything (and not just forms!)

Code snippet I found somewhere in my archive: jQuery.serializeAnything() is a jQuery Extension that serializes any element you specify, in contrast to jQuery’s builtin serialize() function which is limited to serializing form elements only.

Continue readingjQuery.serializeAnything : Serialize anything (and not just forms!)”