S3 PUT 不适用于 javascript 中的预签名 URL
S3 PUT doesn't work with pre-signed URL in javascript
我已经在 java 中为 HTTP PUT 方法生成了预签名的 S3 URL。
URL 的略微修改版本:
https://s3.amazonaws.com/somebucket/pre-signed-url-key-2?AWSAccessKeyId=BKIAIQ6H5Z7BG6KZ2ZUA&Expires=1425617244&Signature=GWcRM5ZIrAKnMJBsxyfm%2F9fyuBk%3D
我知道它是一个有效的预签名 url 因为我可以将它与 curl
一起使用来上传文件
curl -v --upload-file somefile.txt "https://s3.amazonaws.com/somebucket/pre-signed-url-key-2?AWSAccessKeyId=BKIAIQ6H5Z7BG6KZ2ZUA&Expires=1425617244&Signature=GWcRM5ZIrAKnMJBsxyfm%2F9fyuBk%3D"
当我尝试使用以下 Javascript 将文件上传到同一个 URL 时:
function ajaxUsingPresignedUrlPut() {
$.ajax({
url: presignedURLPUT,
type: 'PUT',
data: 'blah blah blah',
success: function () {
console.log('Uploaded data successfully.');
}
}).done(function (data) {
console.log("DONE : " + data);
}).fail(function (e, r) {
console.log("FAIL");
});
}
我收到 403 状态和响应文本
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>BKIAIQ6H5Z7BG6KZ2ZUA</AWSAccessKeyId><StringToSign>PUT
application/x-www-form-urlencoded; charset=UTF-8
1425617244
/somebucket/pre-signed-url-key-2</StringToSign><SignatureProvided>GWcRM5ZIrAKnMJAsxyfm/9fyuAk=</SignatureProvided><StringToSignBytes>50 55 54 0a 0a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 77 77 77 2d 66 6f 72 6d 2d 75 72 6c 65 6e 63 6f 64 65 64 3b 20 63 68 61 72 73 65 74 3d 55 54 46 2d 38 0a 31 34 32 35 36 31 37 32 34 34 0a 2f 6e 6b 6f 6e 64 72 61 74 5f 62 75 63 6b 65 74 2f 70 72 65 2d 73 69 67 6e 65 64 2d 75 72 6c 2d 6b 65 79 2d 32</StringToSignBytes><RequestId>3C8298CAC404C6F5</RequestId><HostId>uCkzA//CdCLi4SINifkIe0WH6GOlCJBgFlN8ghx8NnULEe+QVslsdoUsJc4AUdA8</HostId></Error>
我在 S3 存储桶上配置了以下 CORS 策略:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>ETag</ExposeHeader>
<ExposeHeader>Content-Length</ExposeHeader>
<ExposeHeader>Content-Type</ExposeHeader>
<ExposeHeader>Connection</ExposeHeader>
<ExposeHeader>Date</ExposeHeader>
<ExposeHeader>Server</ExposeHeader>
<ExposeHeader>x-amz-delete-marker</ExposeHeader>
<ExposeHeader>x-amz-id-2</ExposeHeader>
<ExposeHeader>x-amz-request-id</ExposeHeader>
<ExposeHeader>x-amz-version-id</ExposeHeader>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
同样的 java 脚本代码适用于预签名 GET URL:
https://s3.amazonaws.com/somebucket/pre-signed-url-key-2?AWSAccessKeyId=BKIBIQ6H5Z7LG6KZ2ZUB&Expires=1425616588&Signature=qMPpuzMwxonYPDQETdLafEQGqMU%3D
function ajaxUsingPresignedUrlGET() {
$.ajax({
url: presignedURLGET,
type: 'GET',
success: function () {
console.log('Uploaded data successfully.');
}
}).done(function (data) {
console.log("DONE : " + data);
}).fail(function (e, r) {
console.log("FAIL");
});
}
我现在唯一的猜测是 URL 是 PUT url 包含 %2F
并查看代表 /
的 http://www.w3schools.com/tags/ref_urlencode.asp这可能会导致浏览器出现问题?或者,也许我完全离开这里,还有其他一些我想念的问题。
编辑 #1 添加 java 用于生成预签名的代码 URL
public S3GeneratePresignedUrlDemo(HttpMethod httpMethod) {
this.httpMethod = httpMethod;
System.out.println("HTTP METHOD : " + this.httpMethod);
}
@Override
public void goImpl() throws Exception {
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, KEY);
request.setMethod(httpMethod);
request.setExpiration(createDateNHoursFromNow(24));
URL url = s3Client.generatePresignedUrl(request);
System.out.println(url);
}
private static Date createDateNHoursFromNow(int hours){
Date date = new Date();
long millis = date.getTime();
final int millisInSecond = 1000;
final int secondsInMinute = 60;
final int minutesInHour = 60;
millis += millisInSecond * secondsInMinute * minutesInHour * hours;
date.setTime(millis);
return date;
}`
添加
headers: {'Content-Type': 'text/plain;charset=UTF-8'},
到AJAX
function ajaxUsingPresignedUrlPut() {
$.ajax({
url: presignedURLPUT,
type: 'PUT',
data: 'blah blah blah',
headers: {'Content-Type': 'text/plain;charset=UTF-8'},
success: function () {
console.log('Uploaded data successfully.');
}
}).done(function (data) {
console.log("DONE : " + data);
}).fail(function (arg1, arg2) {
console.log("FAIL");
});
}
并在生成 URL 时设置内容类型解决了问题
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, KEY);
request.setContentType("text/plain;charset=UTF-8");
//...
实际上有一个更好的方法来设置内容类型,这似乎是这里的问题:
$.ajax({
url : s3presignedUrl,
type : "PUT",
data : file,
dataType : "text",
cache : false,
contentType : file.type,
processData : false
})
对您来说重要的部分是 contentType : file.type
,其中 file.type 来自 <input type=file>
onchange 事件(它实际上揭示了 this.files
然后可以对其进行迭代...... )
您可以在此处从我的 php+js 解决方案中获取完整的客户端文件:https://github.com/JoernBerkefeld/s3SignedUpload
如果您不想像 Nickolay Kondratyev 建议的那样限制内容类型,您可以像这样设置内容类型:
headers: {'Content-Type': ' '},
注意,有一个带有 space 字符的字符串。非空、非未定义、空字符串或任何其他虚假值。并且有效。
更新:这不再有效,S3 现在检测 mime 类型并添加它
我已经在 java 中为 HTTP PUT 方法生成了预签名的 S3 URL。
URL 的略微修改版本:
https://s3.amazonaws.com/somebucket/pre-signed-url-key-2?AWSAccessKeyId=BKIAIQ6H5Z7BG6KZ2ZUA&Expires=1425617244&Signature=GWcRM5ZIrAKnMJBsxyfm%2F9fyuBk%3D
我知道它是一个有效的预签名 url 因为我可以将它与 curl
一起使用来上传文件
curl -v --upload-file somefile.txt "https://s3.amazonaws.com/somebucket/pre-signed-url-key-2?AWSAccessKeyId=BKIAIQ6H5Z7BG6KZ2ZUA&Expires=1425617244&Signature=GWcRM5ZIrAKnMJBsxyfm%2F9fyuBk%3D"
当我尝试使用以下 Javascript 将文件上传到同一个 URL 时:
function ajaxUsingPresignedUrlPut() {
$.ajax({
url: presignedURLPUT,
type: 'PUT',
data: 'blah blah blah',
success: function () {
console.log('Uploaded data successfully.');
}
}).done(function (data) {
console.log("DONE : " + data);
}).fail(function (e, r) {
console.log("FAIL");
});
}
我收到 403 状态和响应文本
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>BKIAIQ6H5Z7BG6KZ2ZUA</AWSAccessKeyId><StringToSign>PUT
application/x-www-form-urlencoded; charset=UTF-8
1425617244
/somebucket/pre-signed-url-key-2</StringToSign><SignatureProvided>GWcRM5ZIrAKnMJAsxyfm/9fyuAk=</SignatureProvided><StringToSignBytes>50 55 54 0a 0a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 77 77 77 2d 66 6f 72 6d 2d 75 72 6c 65 6e 63 6f 64 65 64 3b 20 63 68 61 72 73 65 74 3d 55 54 46 2d 38 0a 31 34 32 35 36 31 37 32 34 34 0a 2f 6e 6b 6f 6e 64 72 61 74 5f 62 75 63 6b 65 74 2f 70 72 65 2d 73 69 67 6e 65 64 2d 75 72 6c 2d 6b 65 79 2d 32</StringToSignBytes><RequestId>3C8298CAC404C6F5</RequestId><HostId>uCkzA//CdCLi4SINifkIe0WH6GOlCJBgFlN8ghx8NnULEe+QVslsdoUsJc4AUdA8</HostId></Error>
我在 S3 存储桶上配置了以下 CORS 策略:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>ETag</ExposeHeader>
<ExposeHeader>Content-Length</ExposeHeader>
<ExposeHeader>Content-Type</ExposeHeader>
<ExposeHeader>Connection</ExposeHeader>
<ExposeHeader>Date</ExposeHeader>
<ExposeHeader>Server</ExposeHeader>
<ExposeHeader>x-amz-delete-marker</ExposeHeader>
<ExposeHeader>x-amz-id-2</ExposeHeader>
<ExposeHeader>x-amz-request-id</ExposeHeader>
<ExposeHeader>x-amz-version-id</ExposeHeader>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
同样的 java 脚本代码适用于预签名 GET URL:
https://s3.amazonaws.com/somebucket/pre-signed-url-key-2?AWSAccessKeyId=BKIBIQ6H5Z7LG6KZ2ZUB&Expires=1425616588&Signature=qMPpuzMwxonYPDQETdLafEQGqMU%3D
function ajaxUsingPresignedUrlGET() {
$.ajax({
url: presignedURLGET,
type: 'GET',
success: function () {
console.log('Uploaded data successfully.');
}
}).done(function (data) {
console.log("DONE : " + data);
}).fail(function (e, r) {
console.log("FAIL");
});
}
我现在唯一的猜测是 URL 是 PUT url 包含 %2F
并查看代表 /
的 http://www.w3schools.com/tags/ref_urlencode.asp这可能会导致浏览器出现问题?或者,也许我完全离开这里,还有其他一些我想念的问题。
编辑 #1 添加 java 用于生成预签名的代码 URL
public S3GeneratePresignedUrlDemo(HttpMethod httpMethod) {
this.httpMethod = httpMethod;
System.out.println("HTTP METHOD : " + this.httpMethod);
}
@Override
public void goImpl() throws Exception {
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, KEY);
request.setMethod(httpMethod);
request.setExpiration(createDateNHoursFromNow(24));
URL url = s3Client.generatePresignedUrl(request);
System.out.println(url);
}
private static Date createDateNHoursFromNow(int hours){
Date date = new Date();
long millis = date.getTime();
final int millisInSecond = 1000;
final int secondsInMinute = 60;
final int minutesInHour = 60;
millis += millisInSecond * secondsInMinute * minutesInHour * hours;
date.setTime(millis);
return date;
}`
添加
headers: {'Content-Type': 'text/plain;charset=UTF-8'},
到AJAX
function ajaxUsingPresignedUrlPut() {
$.ajax({
url: presignedURLPUT,
type: 'PUT',
data: 'blah blah blah',
headers: {'Content-Type': 'text/plain;charset=UTF-8'},
success: function () {
console.log('Uploaded data successfully.');
}
}).done(function (data) {
console.log("DONE : " + data);
}).fail(function (arg1, arg2) {
console.log("FAIL");
});
}
并在生成 URL 时设置内容类型解决了问题
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, KEY);
request.setContentType("text/plain;charset=UTF-8");
//...
实际上有一个更好的方法来设置内容类型,这似乎是这里的问题:
$.ajax({
url : s3presignedUrl,
type : "PUT",
data : file,
dataType : "text",
cache : false,
contentType : file.type,
processData : false
})
对您来说重要的部分是 contentType : file.type
,其中 file.type 来自 <input type=file>
onchange 事件(它实际上揭示了 this.files
然后可以对其进行迭代...... )
您可以在此处从我的 php+js 解决方案中获取完整的客户端文件:https://github.com/JoernBerkefeld/s3SignedUpload
如果您不想像 Nickolay Kondratyev 建议的那样限制内容类型,您可以像这样设置内容类型:
headers: {'Content-Type': ' '},
注意,有一个带有 space 字符的字符串。非空、非未定义、空字符串或任何其他虚假值。并且有效。
更新:这不再有效,S3 现在检测 mime 类型并添加它