jQuery 使用 CORS headers 将文件上传到 S3(和 rails)
jQuery file upload to S3 (and rails) with CORS headers
我正在尝试将文件直接上传到 S3,并在上传时显示进度条。
但是当我提交表单时,我收到以下错误消息:
OPTIONS https://s3-eu-west-1.amazonaws.com/my-bucket 403 (Forbidden)
9:1 XMLHttpRequest cannot load
https://s3-eu-west-1.amazonaws.com/my-bucket. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'https://localhost:3000' is therefore not allowed
access. The response had HTTP status code 403. 9:223 fail
这是我的表格:
%form#file_upload(action=@aws_s3_url method="post" enctype="multipart/form-data")
-# order is important!
-# also, the things that are not filled in right now *will* be filled in soon. See below.
%input{:type => :hidden, :name => :key}
%input{:type => :hidden, :name => "AWSAccessKeyId", :value => "ACCESS_KEY"}
%input{:type => :hidden, :name => :acl, :value => :private}
%input{:type => :hidden, :name => :success_action_redirect}
%input{:type => :hidden, :name => :policy}
%input{:type => :hidden, :name => :signature}
.fileupload-content
.fileupload-progress
.file-upload
%label.fileinput-button
%span Upload Document
%input{:type => :file, :name => :file}
这是我的 javascript 代码,使用 jquery-file-upload
插件:
$(function() {
$('#file_upload').fileupload({
//forceIframeTransport: true, // VERY IMPORTANT. you will get 405 Method Not Allowed if you don't add this.
autoUpload: true,
type: 'POST',
dataType: 'xml',
url: $(this).attr('action'),
add: function (event, data) {
$.ajax({
url: "/projects/9/create_file",
type: 'GET',
dataType: 'json',
data: {doc: {title: data.files[0].name}},
async: false,
success: function(retdata) {
// after we created our document in rails, it is going to send back JSON of they key,
// policy, and signature. We will put these into our form before it gets submitted to amazon.
$('#file_upload').find('input[name=key]').val(retdata.key);
$('#file_upload').find('input[name=policy]').val(retdata.policy);
$('#file_upload').find('input[name=signature]').val(retdata.signature);
$('#file_upload').find('input[name=success_action_redirect]').val(retdata.success_action_redirect);
}
});
console.log(data)
file = data.files[0]
data.context = $(tmpl("template-upload", file))
$('#file_upload').append(data.context)
data.submit();
},
progress: function (event, data) {
progress = parseInt(data.loaded / data.total * 100, 10)
console.log("Progress")
data.context.find('.bar').css('width', progress + '%')
},
send: function(e, data) {
// show a loading spinner because now the form will be submitted to amazon,
// and the file will be directly uploaded there, via an iframe in the background.
//$('#loading').show();
console.log("Loading")
},
fail: function(e, data) {
console.log('fail');
console.log(data);
},
done: function (event, data) {
// here you can perform an ajax call to get your documents to display on the screen.
$('#your_documents').load("/documents?for_item=1234");
// hide the loading spinner that we turned on earlier.
$('#loading').hide();
},
});
});
我知道代码有效,因为当我取消对 forceIframeTransport 的注释时,它会处理请求。问题是使用该方法您只能获得一次进度事件(完成时),因此它违背了拥有进度条的目的。我读到没有 forceIframeTransport,你必须设置 CORS headers,我在 rails 中这样做:
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
#, DELETE, OPTIONS
#DELETE, OPTIONS
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = 'https://0.0.0.0:3000/*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT'
headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, Token'
headers['Access-Control-Max-Age'] = '3000'
end
def cors_preflight_check
if request.method == 'OPTIONS'
headers['Access-Control-Allow-Origin'] = 'https://0.0.0.0:3000/*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT'
headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version, Token'
headers['Access-Control-Max-Age'] = '3000'
render :text => '', :content_type => 'text/plain'
end
end
它们确实被发送了,因为当我手动执行 GET“/projects/9/create_file”(例如使用 advance rest 插件)时,我得到以下响应 headers:
Access-Control-Allow-Origin: https://0.0.0.0:3000/*
Access-Control-Allow-Methods: POST, GET, PUT
Access-Control-Allow-Headers: Origin, Content-Type, Accept,
Authorization, Token Access-Control-Max-Age: 3000
此外,这是我在亚马逊的 CORS 配置:
<CORSRule>
<AllowedOrigin>https://0.0.0.0:3000/*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
有什么可能出错的想法吗?
要允许 https://example.com
上的任何子路径,您可以这样做:
<CORSRule>
<AllowedOrigin>https://example.com</AllowedOrigin>
</CORSRule>
origin header field 可以是通配符 (*) 或包含一个或不包含通配符的 url。例如:
https://*.example.com
然而,在末尾添加通配符 https://0.0.0.0:3000/*
将不允许任何可能猜到的路径。
我正在尝试将文件直接上传到 S3,并在上传时显示进度条。 但是当我提交表单时,我收到以下错误消息:
OPTIONS https://s3-eu-west-1.amazonaws.com/my-bucket 403 (Forbidden) 9:1 XMLHttpRequest cannot load
https://s3-eu-west-1.amazonaws.com/my-bucket. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:3000' is therefore not allowed access. The response had HTTP status code 403. 9:223 fail
这是我的表格:
%form#file_upload(action=@aws_s3_url method="post" enctype="multipart/form-data")
-# order is important!
-# also, the things that are not filled in right now *will* be filled in soon. See below.
%input{:type => :hidden, :name => :key}
%input{:type => :hidden, :name => "AWSAccessKeyId", :value => "ACCESS_KEY"}
%input{:type => :hidden, :name => :acl, :value => :private}
%input{:type => :hidden, :name => :success_action_redirect}
%input{:type => :hidden, :name => :policy}
%input{:type => :hidden, :name => :signature}
.fileupload-content
.fileupload-progress
.file-upload
%label.fileinput-button
%span Upload Document
%input{:type => :file, :name => :file}
这是我的 javascript 代码,使用 jquery-file-upload
插件:
$(function() {
$('#file_upload').fileupload({
//forceIframeTransport: true, // VERY IMPORTANT. you will get 405 Method Not Allowed if you don't add this.
autoUpload: true,
type: 'POST',
dataType: 'xml',
url: $(this).attr('action'),
add: function (event, data) {
$.ajax({
url: "/projects/9/create_file",
type: 'GET',
dataType: 'json',
data: {doc: {title: data.files[0].name}},
async: false,
success: function(retdata) {
// after we created our document in rails, it is going to send back JSON of they key,
// policy, and signature. We will put these into our form before it gets submitted to amazon.
$('#file_upload').find('input[name=key]').val(retdata.key);
$('#file_upload').find('input[name=policy]').val(retdata.policy);
$('#file_upload').find('input[name=signature]').val(retdata.signature);
$('#file_upload').find('input[name=success_action_redirect]').val(retdata.success_action_redirect);
}
});
console.log(data)
file = data.files[0]
data.context = $(tmpl("template-upload", file))
$('#file_upload').append(data.context)
data.submit();
},
progress: function (event, data) {
progress = parseInt(data.loaded / data.total * 100, 10)
console.log("Progress")
data.context.find('.bar').css('width', progress + '%')
},
send: function(e, data) {
// show a loading spinner because now the form will be submitted to amazon,
// and the file will be directly uploaded there, via an iframe in the background.
//$('#loading').show();
console.log("Loading")
},
fail: function(e, data) {
console.log('fail');
console.log(data);
},
done: function (event, data) {
// here you can perform an ajax call to get your documents to display on the screen.
$('#your_documents').load("/documents?for_item=1234");
// hide the loading spinner that we turned on earlier.
$('#loading').hide();
},
});
});
我知道代码有效,因为当我取消对 forceIframeTransport 的注释时,它会处理请求。问题是使用该方法您只能获得一次进度事件(完成时),因此它违背了拥有进度条的目的。我读到没有 forceIframeTransport,你必须设置 CORS headers,我在 rails 中这样做:
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
#, DELETE, OPTIONS
#DELETE, OPTIONS
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = 'https://0.0.0.0:3000/*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT'
headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, Token'
headers['Access-Control-Max-Age'] = '3000'
end
def cors_preflight_check
if request.method == 'OPTIONS'
headers['Access-Control-Allow-Origin'] = 'https://0.0.0.0:3000/*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT'
headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version, Token'
headers['Access-Control-Max-Age'] = '3000'
render :text => '', :content_type => 'text/plain'
end
end
它们确实被发送了,因为当我手动执行 GET“/projects/9/create_file”(例如使用 advance rest 插件)时,我得到以下响应 headers:
Access-Control-Allow-Origin: https://0.0.0.0:3000/*
Access-Control-Allow-Methods: POST, GET, PUT
Access-Control-Allow-Headers: Origin, Content-Type, Accept,
Authorization, Token Access-Control-Max-Age: 3000
此外,这是我在亚马逊的 CORS 配置:
<CORSRule>
<AllowedOrigin>https://0.0.0.0:3000/*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
有什么可能出错的想法吗?
要允许 https://example.com
上的任何子路径,您可以这样做:
<CORSRule>
<AllowedOrigin>https://example.com</AllowedOrigin>
</CORSRule>
origin header field 可以是通配符 (*) 或包含一个或不包含通配符的 url。例如:
https://*.example.com
然而,在末尾添加通配符 https://0.0.0.0:3000/*
将不允许任何可能猜到的路径。