Blueimp File Upload - Multiple Uploads Directly to S3
Asked Answered
G

1

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

Gregoriogregorius answered 19/7, 2016 at 20:9 Comment(10)
Did you find an answer to this problem?Teletype
Unfortunately, no. We just had to disable multiple file uploads.Gregoriogregorius
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.Teletype
Is using File-Upload plugin part of requirement?Karbala
Looks like you have placed data.submit(); //why is this submitting all files at once? inside the success callbackSealy
Can you check data object before data.submit(); . Does it has only one file in files array?Nicknack
@Teletype - can you check the proposed answer and see if it solves the issue? I don't have S3 server to test with.Roxieroxine
I'll try to test it soon, if @Gregoriogregorius says it's now working for him before I have a chance to test I'll award the bounty too.Teletype
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?Roxieroxine
No problem. I added a link to a full html page at the bottom of the question. Thanks so much for helping with this!Gregoriogregorius
D
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

Dehypnotize answered 16/11, 2016 at 13:5 Comment(8)
Thanks for responding promptly and in detail to the email Sebastian.Roxieroxine
Greenracoon23 can you check and confirm the solution?Roxieroxine
It works! Thanks! Specifically, it was the <AllowedOrigin> CORSRule. Your plugin is awesome btw.Gregoriogregorius
Great news @Gregoriogregorius - lovely to hear that long running issue now has a solution. I have awarded the bounty to Seb.Roxieroxine
Don't you also have to set limitMultiFileUploads: 1? Otherwise I still see only one post request.Partible
Another problem is that S3 POST doesn't return json in the expected format so it's not updating the table correctly...Partible
What if I want to trigger the upload on button click, and not submit it in the fileaddfunction. I am struggling with this since 3 days already.Hedrick
The final link to the integration example is broken, but here's the most recent version (from April 2018) on the Internet ArchiveRaphaelraphaela

© 2022 - 2024 — McMap. All rights reserved.