15

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

GreenRaccoon23
  • 3,603
  • 7
  • 32
  • 46
  • 2
    Did you find an answer to this problem? – Msencenb Oct 30 '16 at 04:25
  • 1
    Unfortunately, no. We just had to disable multiple file uploads. – GreenRaccoon23 Oct 31 '16 at 19:45
  • 1
    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 Nov 04 '16 at 22:32
  • 1
    Is using File-Upload plugin part of requirement? – guest271314 Nov 05 '16 at 06:00
  • 1
    Looks like you have placed `data.submit(); //why is this submitting all files at once?` inside the success callback – Kiran Shakya Nov 05 '16 at 07:05
  • Can you check `data` object before `data.submit();` . Does it has only one file in `files` array? – Qsprec Nov 09 '16 at 06:17
  • @Msencenb - can you check the proposed answer and see if it solves the issue? I don't have S3 server to test with. – Vanquished Wombat Nov 09 '16 at 17:11
  • I'll try to test it soon, if @GreenRaccoon23 says it's now working for him before I have a chance to test I'll award the bounty too. – Msencenb Nov 09 '16 at 19:21
  • 1
    So I started to set up a debugging config for this. My strategy is to get a standalone HTML + JS page (no server-side) set up then dig into what the state of the data object is a files are added. Blueimp's code is not minified and I can already see there are some handy console outputs ready to de-comment. However, without seeing the HTML side of your case I am a little bit in the dark. I'd like to do the work on this but could you maybe help me out by cutting down the case to a standalone HTML and JS setup that illustrates the issue? – Vanquished Wombat Nov 15 '16 at 12:46
  • No problem. I added a link to a full html page at the bottom of the question. Thanks so much for helping with this! – GreenRaccoon23 Nov 15 '16 at 16:21

1 Answers1

11

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

MarredCheese
  • 17,541
  • 8
  • 92
  • 91
Sebastian Tschan
  • 1,444
  • 15
  • 12