15
votes

After searching the past couple days, I've found nearly 30 different people asking this same question, and I haven't found an answer. A couple reported that they found a solution, but did not provide it, so I'm hoping someone could answer it here.


Using blueimp jQuery File Upload, how can you upload multiple files directly to Amazon S3?

Problem: S3 accepts only one file per request.

Solution: Use blueimp jQuery File Upload to send separate requests for each file.

Roadblock: I cannot figure out how to make blueimp jQuery File Upload do this.


Solution Attempt

The guide is here: https://github.com/blueimp/jQuery-File-Upload/wiki/Upload-directly-to-S3

S3 currently requires more form fields than the ones shown in the guide. Here's a trimmed down version of my code:

$(':file').each(function(i, el) {

    var fileInput = $(el);
    var form = fileInput.parents('form:first');

    fileInput.fileupload({
        forceIframeTransport: true,
        autoUpload: true,
        singleFileUploads: true, //default anyway
        add: function(event, data) {
            var files = data.files || [];
            var nextInQueue = files[0]; //this is a queue, not a list
            console.log('nextInQueue:', nextInQueue);
            if (!nextInQueue) return;

            var fileData = {name: nextInQueue.name, mime: nextInQueue.type, size: nextInQueue.size};

            $.ajax({
                url: '/s3stuff',
                type: 'POST',
                dataType: 'json',
                data: {'file': fileData},
                async: false,
                success: function(res) {
                    form.find('input[name="key"]').val(res.key);
                    form.find('input[name="AWSAccessKeyId"]').val(res.AWSAccessKeyId);
                    form.find('input[name="policy"]').val(res.policy);
                    form.find('input[name="signature"]').val(res.signature);
                    form.find('input[name="acl"]').val(res.acl);
                    form.find('input[name="success_action_status"]').val(res.success_action_status);
                    form.find('input[name="Content-Type"]').val(nextInQueue.type);
                    form.attr('action', res.url);

                    data.submit(); //why is this submitting all files at once?
                }
            });
        },
        fail: function(err) {
            console.log('err:', err.stack);
        }
    });
});

Error

When I try to upload a single file, it works great! But when I try to upload multiple files, S3 returns this 400 error:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>InvalidArgument</Code>
    <Message>POST requires exactly one file upload per request.</Message>
    <ArgumentName>file</ArgumentName>
    <ArgumentValue>2</ArgumentValue>
</Error>

Analysis

The blueimp jQuery File Upload documentation says this (under add):

If the singleFileUploads option is enabled (which is the default), the add callback will be called once for each file in the selection for XHR file uploads, with a data.files array length of one, as each file is uploaded individually.

This is where I'm stuck:
If "each file is uploaded individually" to S3, why does S3 say otherwise?

Also, regardless of the number of files, the add function runs only once. (I verify this with the console.log statement.) I see 2 likely reasons for this:

  1. The plugin stops further submissions after one fails.
  2. The plugin is not submitting files individually (for whatever reason).

Is my code incorrect, am I missing an option in the plugin, or does the plugin not support multiple direct uploads to S3?


Update

Here's an html file for quick testing: http://pastebin.com/mUBgr4MP

1
Did you find an answer to this problem? - Msencenb
Unfortunately, no. We just had to disable multiple file uploads. - GreenRaccoon23
Ok no worries, I've started a bounty on this as my requirements must support multiple file uploads and you've described my problem precisely. Hopefully we both get an answer. - Msencenb
Is using File-Upload plugin part of requirement? - guest271314
Looks like you have placed data.submit(); //why is this submitting all files at once? inside the success callback - Kiran Shakya

1 Answers

11
votes

Author of JQuery File Upload here.

The reason the files are not submitted individually is the forceIframeTransport option, which is set to true in your example.

The documentation for the singleFileUploads option states the following:

By default, each file of a selection is uploaded using an individual request for XHR type uploads.

While the documentation for the forceIframeTransport option states the following:

Set this option to true to force iframe transport uploads, even if the browser is capable of XHR file uploads.

So, the solution is to enable CORS on the S3 bucket and to not enable the forceIframeTransport option.

By the way, most of the integration examples are user contributions (including the S3 upload guides). Here's another one which uses the S3 CORS feature and might help your out (haven't tested it though): http://pjambet.github.io/blog/direct-upload-to-s3