![](/img/trans.png)
[英]Rails 6 Amazon S3 Error POST 403 (Forbidden) using s3_direct_upload gem
[英]Direct upload to amazon S3 using jquery.fileupload
在我的應用程序中,我希望能夠直接從瀏覽器將文件上傳到我的AWS S3存儲桶。 我的后端是rails,但我想避免跳到服務器的額外負擔,並避免使用回形針,carrierwave,carrierwave_direct等寶石,以使其簡單。 我大致跟隨此教程從Heroku的。
我正在使用aws-sdk gem和jquery.fileupload.js庫。
問題是,當我嘗試上傳時,我從AWS收到了一個400錯誤的請求。
我認為這不是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
回調將觸發並綁定表單的Submit操作,這與預期的一樣。 提交表單后,請求將轉到S3,但由於返回了400,因此fail
觸發fail
回調。
這個問題可能是描述問題所在,但根據所提供的信息,我無法解決該問題。
以下是從Chrome開發者工具復制的請求/響應信息。
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沒有看到關鍵字段,如此答案所示 。
一些相關的寶石:
* 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
我不確定如何使它正常工作。
提前致謝!
我找到了解決方案。
我在前端捕獲的在后端生成的表單數據:
upload_form_data = '<%= escape_javascript(@presigned_post.fields.to_json.html_safe) %>'
必須從JSON字符串轉換為JavaScript對象:
upload_form_data_obj = JSON.parse(upload_form_data);
顯然, $.fileupload
函數需要一個formData對象,而不是字符串。
進行此更改后,所需的表單數據將包含在S3的POST中,並且成功。
這是有效的javascript代碼:
$(function() {
var $form = $('form.direct_upload'),
upload_url,
upload_form_data,
upload_form_data_obj;
if ($form.length) {
upload_url = '<%= escape_javascript(@presigned_post.url) %>'
upload_form_data = '<%= escape_javascript(@presigned_post.fields.to_json.html_safe) %>';
upload_form_data_obj = JSON.parse(upload_form_data);
console.log('URL: ', upload_url);
console.log('Form data: ', upload_form_data_obj);
$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_obj, // needed to be an object, not a string
paramName: 'file',
dataType: 'JSON',
add: function(e, data) {
console.log('add callback fired.');
$form.submit(function(e) {
e.preventDefault();
console.log('form submitted.');
console.log(data);
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);
}
});
});
}
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.