Загрузка файла Blueimp - несколько загрузок напрямую на S3

После поиска последних двух дней, я нашел около 30 разных людей, задающих этот же вопрос, и я не нашел ответа. Пара сообщила, что они нашли решение, но не предоставили его, поэтому я надеюсь, что кто-то сможет ответить на него здесь.


Использование Загрузка файла blueimp jQuery, как вы можете загружать несколько файлов напрямую к Amazon S3?

Проблема: S3 принимает только один файл за запрос.

Решение: используйте blueimp jQuery Загрузка файлов для отправки отдельных запросов для каждого файла.

Roadblock: я не могу понять, как сделать blueimp jQuery File Upload сделать это.


Решение Попытка

Путеводитель здесь: https://github.com/blueimp/jQuery-File-Upload/wiki/Upload-directly-to-S3

S3 в настоящее время требует больше полей формы, чем те, которые указаны в руководстве. Здесь урезана версия моего кода:

$(':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);
        }
    });
});

Ошибка

Когда я пытаюсь загрузить один файл, он отлично работает! Но когда я пытаюсь загрузить несколько файлов, S3 возвращает эту ошибку 400:

<?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>

Анализ

документация по загрузке файла blueimp jQuery говорит об этом (под add):

Если опция singleFileUploads включена (которая по умолчанию), вызов обратного вызова будет вызываться один раз для каждого файла в выборе для загрузки файлов XHR с длиной массива data.files, поскольку каждый файл загружается индивидуально.

Вот где я застрял:
Если "each file is uploaded individually" - S3, почему S3 говорит иначе?

Кроме того, независимо от количества файлов функция add запускается только один раз. (Я проверяю это с помощью инструкции console.log.) Я вижу 2 возможных причины для этого:

  • Плагин останавливает дальнейшие представления после того, как один из них завершился неудачей.
  • Плагин не отправляет файлы по отдельности (по какой-либо причине).

Неправильно ли мой код, не хватает ли опции в плагине, или плагин не поддерживает несколько прямых загрузок на S3?


Update

Здесь html файл для быстрого тестирования: http://pastebin.com/mUBgr4MP

Ответ 1

Автор загрузки файла JQuery здесь.

Причиной того, что файлы не передаются по отдельности, является параметр forceIframeTransport, для которого в вашем примере установлено значение true.

Документация для опции singleFileUploads гласит следующее:

По умолчанию каждый файл выбора загружается с использованием отдельного запроса на загрузку типа XHR.

В то время как документация для опции forceIframeTransport гласит следующее:

Установите для этого параметра значение true чтобы принудительно загружать транспорт iframe, даже если браузер поддерживает загрузку файлов XHR.

Таким образом, решение состоит в том, чтобы включить CORS в forceIframeTransport S3 и не включать опцию forceIframeTransport.

Кстати, большинство примеров интеграции - это вклады пользователей (включая руководства по загрузке S3). Вот еще один, который использует функцию S3 CORS и может помочь вам (хотя еще не проверял): http://pjambet.github.io/blog/direct-upload-to-s3