В моем приложении я хотел бы иметь возможность загружать файлы непосредственно из браузера в мой ковш AWS S3. Мой бэкэнд - рельсы, но я бы хотел избежать лишнего хопа на моем сервере и избегать использования драгоценных камней, таких как paperclip, carrierwave, carrierwave_direct и т. Д., Чтобы это было просто. Я примерно следую за этим tutorial от heroku.Прямая загрузка в amazon S3 с помощью jquery.fileupload
Я использую драгоценный камень aws-sdk и jquery.fileupload.js lib.
Проблема в том, что когда я пытаюсь выполнить загрузку, я возвращаю обратно 400 запросов с AWS.
Я не думаю, что это проблема CORS. У меня есть CORS, настроенный на моем ковше, и я вижу успешный запрос OPTIONS, за которым следует запрос POST для загрузки файла, который возвращает 400 плохих запросов.
Ниже приведена упрощенная демоверсия, которая реплицирует проблему.
Это действие контроллера. Он генерирует объект AWS :: S3 :: PresignedPost, чтобы представление могло использовать его для отправки файлов непосредственно на S3.
def new
Aws.config.update({
region: 'us-east-1',
credentials: Aws::Credentials.new('[FILTERED]', '[FILTERED]'),
})
s3 = Aws::S3::Resource.new
bucket = s3.bucket('mybucket')
@presigned_post = bucket.presigned_post(key: "attachments/#{SecureRandom.uuid}/${filename}")
@thing = Thing.new
end
Это мнение, new.html.erb
, который оказывает выше, с формой загрузки и JavaScript для обработки загрузки.
<div class='container'>
<%= form_for(@thing, html: { class: 'direct_upload' }) do |f| %>
<%= f.label 'Thing' %>
<%= f.file_field :attachment_url %>
<%= f.submit %>
<% end %>
</div>
<script type="text/javascript">
$(function() {
var $form = $('form.direct_upload'),
upload_url = '<%= escape_javascript(@presigned_post.url.to_s) %>',
upload_form_data = '<%= escape_javascript(@presigned_post.fields.to_json.html_safe) %>';
console.log('URL: ', upload_url);
console.log('Form data: ', upload_form_data);
if ($form.length) {
$form.find('input[type=file]').each(function(index, input) {
var $file_field = $(input);
$file_field.fileupload({
fileInput: $file_field,
url: upload_url,
type: 'POST',
autoUpload: false,
formData: upload_form_data,
paramName: 'file',
dataType: 'XML',
add: function(e, data) {
console.log('add callback fired.');
$form.submit(function(e) {
e.preventDefault();
console.log('form submitted.');
data.submit();
});
},
start: function(e) {
console.log('start callback fired');
},
done: function(e, data) {
console.log('done callback fired');
},
fail: function(e, data) {
console.log('fail callback fired');
console.log(e);
console.log(data);
}
});
});
}
});
</script>
Это ответ возвращается из S3:
<Error>
<Code>InvalidArgument</Code>
<Message>Bucket POST must contain a field named 'key'. If it is specified, please check the order of the fields.</Message>
<ArgumentName>key</ArgumentName>
<ArgumentValue></ArgumentValue>
<RequestId>[filtered]</RequestId>
<HostId>[filtered]</HostId>
</Error>
При загрузке страницы вы можете увидеть ожидаемый результат в консоли JavaScript:
URL: https://mybucket.s3.amazonaws.com/
Form data: {"key":"attachments/d6313635-9735-4b84-9985-f9f62a036de8/${filename}","policy":"[FILTERED]","x-amz-credential":"[FILTERED]/us-east-1/s3/aws4_request","x-amz-algorithm":"AWS4-HMAC-SHA256","x-amz-date":"20150809T134239Z","x-amz-signature":"[FILTERED]"}
Как вы можете видеть, есть ключевое поле.
Когда вы добавляете файл в поле ввода файла, обратный вызов add
запускает и связывает действие отправки формы, как и ожидалось. Когда форма отправляется, запрос переходит на S3, но затем обратный вызов fail
срабатывает, потому что возвращается 400.
Этот question может описывать, в чем проблема, но я не смог ее решить на основе предоставленной информации.
Ниже приведена информация о запросе/ответе, скопированная с помощью инструментов Chrome dev.
Remote Address:54.231.17.17:443
Request URL:https://mybucket.s3.amazonaws.com/
Request Method:POST
Status Code:400 Bad Request
Response Headers
Access-Control-Allow-Methods:GET, POST, PUT
Access-Control-Allow-Origin:*
Connection:close
Content-Type:application/xml
Date:Sun, 09 Aug 2015 12:29:57 GMT
Server:AmazonS3
Transfer-Encoding:chunked
Vary:Origin, Access-Control-Request-Headers, Access-Control-Request-Method
x-amz-id-2:ymrt0MUlhf3bKqVWj+O5jhaUPXNEXy9HQh9PABmqzDkkb4Ods3Hy1LA++8G/Svri3LcOktpnGeE=
x-amz-request-id:545E755033D285F2
Request Headers
Accept:application/xml, text/xml, */*; q=0.01
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:331
Content-Type:multipart/form-data; boundary=----WebKitFormBoundary9vtTme67oAg1OMyL
Host:braidio.s3.amazonaws.com
Origin:http://localhost:3000
Referer:http://localhost:3000/things/new
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36
Request Payload
------WebKitFormBoundary9vtTme67oAg1OMyL
Content-Disposition: form-data; name="file"; filename="my_text.txt"
Content-Type: text/plain
------WebKitFormBoundary9vtTme67oAg1OMyL--
Как вы можете видеть, полезная нагрузка запроса содержит только файл, а не ключ. Возможно, файл должен появиться после всех остальных полей в запросе на отправку, и поэтому S3 не видит ключевое поле, как это предлагается в этом answer.
Некоторые соответствующие драгоценные камни:
* jquery-rails (4.0.4)
* rails (4.2.3)
* aws-sdk (2.1.13)
* aws-sdk-core (2.1.13)
* aws-sdk-resources (2.1.13)
Также с помощью jquery.fileupload.js 5.42.3
Я не не знаю, как получить эту работу.
Заранее благодарен!