AWS.S3.upload() 尝试分段上传时出现 403 错误
AWS.S3.upload() 403 Error When Attempting Multipart Upload
TL;DR
尝试在浏览器中使用 AWS SDK for Javascript 提供的 s3.upload()
方法直接从浏览器上传文件时 结合临时 通过调用 AWS.STS.getFederationToken()
生成的 IAM 凭据 对于 non-multipart 上传以及分段上传的 first 部分一切正常。
但是当 s3.upload()
尝试发送分段上传的第二部分时 S3 响应 403 Access Denied
错误。
为什么?
背景
我在我的应用程序中实现了一个上传器,它将支持直接从浏览器到我的 S3 存储桶的分段(分块)上传。
为此,我使用了 s3.upload()
method of the AWS SDK for Javascript in the Browser, which I understand to be nothing more than sugar for its underlying utilization of new AWS.S3.ManagedUpload()
.
可以在这里找到我正在尝试的简单说明:https://aws.amazon.com/blogs/developer/announcing-the-amazon-s3-managed-uploader-in-the-aws-sdk-for-javascript/
此外,我还使用 AWS.STS.getFederationToken()
作为一种手段,从我的 API 层出售临时 IAM 用户 凭据以授权上传。
1,2,3:
- 用户通过标准 HTML
<input type="file">
. 选择文件来启动上传
- 这会触发对我的 API 层的初始请求,以确保用户在我自己的系统上拥有执行此操作所需的权限,如果是这样,那么我的服务器会调用
AWS.STS.getFederationToken()
Policy
param 将他们的特权范围缩小到无非就是将文件上传到提供的密钥。然后 returns 生成的临时信用到浏览器。
- 现在浏览器有了它需要的临时凭据,它可以开始使用它们来创建一个新的
AWS.S3
客户端,然后执行 AWS.S3.upload()
方法来执行(假定的)自动多部分上传文件。
代码
api.myapp.com/vendUploadCreds.js
这是调用的 API 层方法,用于生成和出售临时上传凭据。在此过程中,帐户已经通过身份验证并被授权接收信用和上传文件。
module.exports = function vendUploadCreds(request, response) {
var account = request.params.account;
var file = request.params.file;
var bucket = 'cdn.myapp.com';
var sts = new AWS.STS({
AccessKeyId : process.env.MY_AWS_ACCESS_KEY_ID,
SecretAccessKey : process.env.MY_AWS_SECRET_ACCESS_KEY
});
/// The following policy is *exactly* the same as the S3 policy
/// attached to the IAM user that executes this STS request.
var policy = {
Version : '2012-10-17',
Statement : [
{
Effect : 'Allow',
Action : [
's3:ListBucket',
's3:ListBucketMultipartUploads',
's3:ListBucketVersions',
's3:ListMultipartUploadParts',
's3:AbortMultipartUpload',
's3:GetObject',
's3:GetObjectVersion',
's3:PutObject',
's3:PutObjectAcl',
's3:PutObjectVersionAcl',
's3:DeleteObject',
's3:DeleteObjectVersion'
],
Resource : [
'arn:aws:s3:::' + bucket + '/' + account._id + '/files/' + file.name
],
Condition : {
StringEquals : {
's3:x-amz-acl' : ['private']
}
}
}
]
};
sts.getFederationToken({
DurationSeconds : 129600, /// 36 hours
Name : account._id + '-uptoken',
Policy : JSON.stringify(policy)
}, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
response.send(data);
});
}
console.myapp.com/uploader.js
这是 browser-side 上的上传器的截断图,它首先调用 vendUploadCreds
API 方法,然后使用生成的临时凭据执行分段上传。
uploader.getUploadCreds(account, file) {
/// A request is sent to api.myapp.com/vendUploadCreds
/// Upon successful response, the creds are returned.
request('https://api.myapp.com/vendUploadCreds', {
params : {
account : account,
file : file
}
}, function(error, data) {
upload.credentials = data.credentials;
this.uploadFile(upload);
});
}
uploader.uploadFile : function(upload) {
var uploadID = upload.id;
/// The `upload` object coming through via the args has
/// a `credentials` property containing the creds obtained
/// via the `vendUploadCreds` method above.
var credentials = new AWS.Credentials({
accessKeyId : upload.credentials.AccessKeyId,
secretAccessKey : upload.credentials.SecretAccessKey,
sessionToken : upload.credentials.SessionToken
});
AWS.config.region = 'us-east-1';
var s3 = new AWS.S3({
credentials,
signatureVersion : 'v2', /// 'v4' also attempted
params : {
Bucket : 'cdn.myapp.com'
}
});
var uploader = s3.upload({
Key : upload.key,
ACL : 'private',
ContentType : upload.file.type,
Body : upload.file
},{
queueSize : 3,
partSize : 1024 * 1024 * 5
});
uploader.on('httpUploadProgress', function(event) {
var total = event.total;
var loaded = event.loaded;
var percent = loaded / total;
percent = Math.ceil(percent * 100);
console.log('Uploaded ' + percent + '% of ' + upload.key);
});
uploader.send(function(error, result) {
console.log(error, result);
});
}
cdn.myapp.com S3 存储桶 CORS 配置
据我所知,这是完全开放的,所以 CORS 应该不是问题?
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>ETag</ExposeHeader>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
错误
好的,所以当我尝试上传文件时,它变得非常混乱:
- 任何小于 5Mb 的文件都可以上传。小于 5Mb(S3 分段上传的最小分段大小)的文件不需要分段上传,因此
s3.upload()
将它们作为标准 PUT 请求发送。有道理,他们成功了。
- 任何超过 5Mb 的文件似乎 都可以上传,但仅限于第一部分。然后当
s3.upload()
尝试发送第二部分时,S3 以 403 Access Denied
错误响应。
我希望你是信息的粉丝,因为这是我在尝试上传 Astrud Gilberto 的忧郁经典 "So Nice (Summer Samba)"(MP3,6.6Mb)时从 Chrome 得到的错误转储:
一般
Request URL:https://s3.amazonaws.com/cdn.myapp.com/5a2cbda70b9b741661ad98df/files/Astrud-Gilberto-So-Nice-1512903188573.mp3?partNumber=2&uploadId=ljaviv9n25aRKwc4HKGhBbbXTWI3wSGZwRRi39fPSEvU2dcM9G7gO6iu5w7va._dMTZil4e_b53Iy5ngojJqRr5F6Uo_ZXuF27yaqizeARmUVf5ZVeah8ZjYwkZV8C0i3rhluYoxFHUPxlLMjaKLww--
Request Method:PUT
Status Code:403 Forbidden
Remote Address:52.216.165.77:443
Referrer Policy:no-referrer-when-downgrade
响应Headers
Access-Control-Allow-Methods:GET, PUT, POST, DELETE
Access-Control-Allow-Origin:*
Access-Control-Expose-Headers:ETag
Access-Control-Max-Age:3000
Connection:close
Content-Type:application/xml
Date:Sun, 10 Dec 2017 10:53:12 GMT
Server:AmazonS3
Transfer-Encoding:chunked
Vary:Origin, Access-Control-Request-Headers, Access-Control-Request-Method
x-amz-id-2:0Mzo7b/qj0r5Is7aJIIJ/U2VxTTulWsjl5kJpTnEhy/B0fQDlRuANcursnxI71LA16AdePVSc/s=
x-amz-request-id:DA008A5116E0058F
请求Headers
Accept:*/*
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.9
Authorization:AWS ASIAJAR5KXKAOPTC64PQ:Wo9lbflZuVVS9+UTTDSjU0iPUbI=
Cache-Control:no-cache
Connection:keep-alive
Content-Length:1314943
Content-Type:application/octet-stream
DNT:1
Host:s3.amazonaws.com
Origin:http://132.12.23.145:8080
Pragma:no-cache
Referer:http://132.12.23.145:8080/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
X-Amz-Date:Sun, 10 Dec 2017 10:53:09 GMT
x-amz-security-token:FQoDYXdzENT//////////wEaDK9srK2+5FN91W+T+SLSA/LdEwpOiY7wDkgggOMhuGEiqIXAQrFMk/EqvZFl8Npqx414WsL9E310rj5mU1RGXsxuN+ers1r6NVPpJIlXSDG7bnwlGabejNvDL9vMX5HJHGbZOEVUoaL60/T5NM+0TZtH61vHAEVmRVFKOB0tSez8TEU1jQ2cJME0THn5RuV/6CuIpA9dlEYO7/ajB5UKT3F1rBkt12b0DeWmKG2pvTJRwa8nrsF6Hk6dk1B1Hl1fUwAh9rD17O9Roi7MFLKisPH+96WX08liC8k+n+kPPOox6ZZM/lOMwlNinDjLc2iC+JD/6uxyAGpNbQ7OHAUsF7DOiMvw6Nv6PrImrBvnK439BhLOk1VXCfxxmtTWGim8TD1w1EciZcJhsuCMpDF8fMnhF/JFw3KNOJXHUtpTGRjNbOPcPojVs3FgIt+9MllIA0pGMr2bYmA3HvKewnhD2qeKkG3DPDIbpwuRoY4wIXCP5OclmoHp5nE5O94aRIvkBvS1YmqDQO+jTiI7/O7vlX63q9sGqdIA4nwzh5ASTRJhC2rKgxepFirEB53dCev8i9f1pwXG3/4H3TvPCLVpK94S7/csNJexJP75bPBpo4nDeIbOBKKIMuUDK1pQsyuGwuUolKS00QU=
X-Amz-User-Agent:aws-sdk-js/2.164.0 callback
查询字符串参数
partNumber:2
uploadId:ljaviv9n25aRKwc4HKGhBbbXTWI3wSGZwRRi39fPSEvU2dcM9G7gO6iu5w7va._dMTZil4e_b53Iy5ngojJqRr5F6Uo_ZXuF27yaqizeARmUVf5ZVeah8ZjYwkZV8C0i3rhluYoxFHUPxlLMjaKLww--
实际响应Body
这是来自 S3 的响应的 body:
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>8277A4969E955274</RequestId><HostId>XtQ2Ezv0Wa81Rm2jymB5ZwTe+OHfwTcnNapYMgceqZCJeb75YwOa1AZZ5/10CAeVgmfeP0BFXnM=</HostId></Error>
问题
- 这显然不是
sts.generateFederationToken()
请求创建的信用的问题,因为如果它是那么较小的 (non-multipart) 上传也会失败,对吗?
cdn.myapp.com
存储桶上的 CORS 配置显然不是问题,因为如果是的话,较小的 (non-multipart) 上传也会失败,对吗?
- 为什么 S3 会接受分段上传的
partNumber=1
,然后在同一上传的 partNumber=2
上接受 403?
一个解决方案
经过几个小时的努力,我发现问题出在我作为 Policy
我的 AWS.STS.getFederationToken()
请求的参数。具体来说,AWS.S3.upload()
只为第一个PUT
请求发送一个x-amz-acl
header,也就是调用S3.initiateMultipartUpoad
.
x-amz-acl
header 未 包含在后续 PUT
上传实际部分的请求中。
我的 IAM 策略 有以下条件,我用它来确保任何上传必须具有 'private' 的 ACL:
Condition : {
StringEquals : {
's3:x-amz-acl' : ['private']
}
}
所以对 S3.initiateMultipartUpload
的初始 PUT
请求没问题,但随后的 PUT
失败了,因为他们没有 x-amz-acl
header .
解决方案是编辑我附加到临时用户的策略并将 s3:PutObject
权限移动到它自己的语句中,然后调整条件以仅在目标值存在时应用。最终政策如下所示:
var policy = {
Version : '2012-10-17',
Statement : [
{
Effect : 'Allow',
Action : [
's3:PutObject'
],
Resource : [
'arn:aws:s3:::' + bucket + '/' + account._id + '/files/' + file.name
],
Condition : {
StringEqualsIfExists : {
's3:x-amz-acl' : ['private']
}
}
},
{
Effect : 'Allow',
Action : [
's3:AbortMultipartUpload'
],
Resource : [
'arn:aws:s3:::' + bucket + '/' + account._id + '/files/' + file.name
]
}
]
};
希望这能帮助其他人避免在这上面浪费三天时间。
TL;DR
尝试在浏览器中使用 AWS SDK for Javascript 提供的s3.upload()
方法直接从浏览器上传文件时 结合临时 通过调用 AWS.STS.getFederationToken()
生成的 IAM 凭据 对于 non-multipart 上传以及分段上传的 first 部分一切正常。
但是当 s3.upload()
尝试发送分段上传的第二部分时 S3 响应 403 Access Denied
错误。
为什么?
背景
我在我的应用程序中实现了一个上传器,它将支持直接从浏览器到我的 S3 存储桶的分段(分块)上传。为此,我使用了 s3.upload()
method of the AWS SDK for Javascript in the Browser, which I understand to be nothing more than sugar for its underlying utilization of new AWS.S3.ManagedUpload()
.
可以在这里找到我正在尝试的简单说明:https://aws.amazon.com/blogs/developer/announcing-the-amazon-s3-managed-uploader-in-the-aws-sdk-for-javascript/
此外,我还使用 AWS.STS.getFederationToken()
作为一种手段,从我的 API 层出售临时 IAM 用户 凭据以授权上传。
1,2,3:
- 用户通过标准 HTML
<input type="file">
. 选择文件来启动上传
- 这会触发对我的 API 层的初始请求,以确保用户在我自己的系统上拥有执行此操作所需的权限,如果是这样,那么我的服务器会调用
AWS.STS.getFederationToken()
Policy
param 将他们的特权范围缩小到无非就是将文件上传到提供的密钥。然后 returns 生成的临时信用到浏览器。 - 现在浏览器有了它需要的临时凭据,它可以开始使用它们来创建一个新的
AWS.S3
客户端,然后执行AWS.S3.upload()
方法来执行(假定的)自动多部分上传文件。
代码
api.myapp.com/vendUploadCreds.js
这是调用的 API 层方法,用于生成和出售临时上传凭据。在此过程中,帐户已经通过身份验证并被授权接收信用和上传文件。
module.exports = function vendUploadCreds(request, response) {
var account = request.params.account;
var file = request.params.file;
var bucket = 'cdn.myapp.com';
var sts = new AWS.STS({
AccessKeyId : process.env.MY_AWS_ACCESS_KEY_ID,
SecretAccessKey : process.env.MY_AWS_SECRET_ACCESS_KEY
});
/// The following policy is *exactly* the same as the S3 policy
/// attached to the IAM user that executes this STS request.
var policy = {
Version : '2012-10-17',
Statement : [
{
Effect : 'Allow',
Action : [
's3:ListBucket',
's3:ListBucketMultipartUploads',
's3:ListBucketVersions',
's3:ListMultipartUploadParts',
's3:AbortMultipartUpload',
's3:GetObject',
's3:GetObjectVersion',
's3:PutObject',
's3:PutObjectAcl',
's3:PutObjectVersionAcl',
's3:DeleteObject',
's3:DeleteObjectVersion'
],
Resource : [
'arn:aws:s3:::' + bucket + '/' + account._id + '/files/' + file.name
],
Condition : {
StringEquals : {
's3:x-amz-acl' : ['private']
}
}
}
]
};
sts.getFederationToken({
DurationSeconds : 129600, /// 36 hours
Name : account._id + '-uptoken',
Policy : JSON.stringify(policy)
}, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
response.send(data);
});
}
console.myapp.com/uploader.js
这是 browser-side 上的上传器的截断图,它首先调用 vendUploadCreds
API 方法,然后使用生成的临时凭据执行分段上传。
uploader.getUploadCreds(account, file) {
/// A request is sent to api.myapp.com/vendUploadCreds
/// Upon successful response, the creds are returned.
request('https://api.myapp.com/vendUploadCreds', {
params : {
account : account,
file : file
}
}, function(error, data) {
upload.credentials = data.credentials;
this.uploadFile(upload);
});
}
uploader.uploadFile : function(upload) {
var uploadID = upload.id;
/// The `upload` object coming through via the args has
/// a `credentials` property containing the creds obtained
/// via the `vendUploadCreds` method above.
var credentials = new AWS.Credentials({
accessKeyId : upload.credentials.AccessKeyId,
secretAccessKey : upload.credentials.SecretAccessKey,
sessionToken : upload.credentials.SessionToken
});
AWS.config.region = 'us-east-1';
var s3 = new AWS.S3({
credentials,
signatureVersion : 'v2', /// 'v4' also attempted
params : {
Bucket : 'cdn.myapp.com'
}
});
var uploader = s3.upload({
Key : upload.key,
ACL : 'private',
ContentType : upload.file.type,
Body : upload.file
},{
queueSize : 3,
partSize : 1024 * 1024 * 5
});
uploader.on('httpUploadProgress', function(event) {
var total = event.total;
var loaded = event.loaded;
var percent = loaded / total;
percent = Math.ceil(percent * 100);
console.log('Uploaded ' + percent + '% of ' + upload.key);
});
uploader.send(function(error, result) {
console.log(error, result);
});
}
cdn.myapp.com S3 存储桶 CORS 配置
据我所知,这是完全开放的,所以 CORS 应该不是问题?
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>ETag</ExposeHeader>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
错误
好的,所以当我尝试上传文件时,它变得非常混乱:
- 任何小于 5Mb 的文件都可以上传。小于 5Mb(S3 分段上传的最小分段大小)的文件不需要分段上传,因此
s3.upload()
将它们作为标准 PUT 请求发送。有道理,他们成功了。 - 任何超过 5Mb 的文件似乎 都可以上传,但仅限于第一部分。然后当
s3.upload()
尝试发送第二部分时,S3 以403 Access Denied
错误响应。
我希望你是信息的粉丝,因为这是我在尝试上传 Astrud Gilberto 的忧郁经典 "So Nice (Summer Samba)"(MP3,6.6Mb)时从 Chrome 得到的错误转储:
一般
Request URL:https://s3.amazonaws.com/cdn.myapp.com/5a2cbda70b9b741661ad98df/files/Astrud-Gilberto-So-Nice-1512903188573.mp3?partNumber=2&uploadId=ljaviv9n25aRKwc4HKGhBbbXTWI3wSGZwRRi39fPSEvU2dcM9G7gO6iu5w7va._dMTZil4e_b53Iy5ngojJqRr5F6Uo_ZXuF27yaqizeARmUVf5ZVeah8ZjYwkZV8C0i3rhluYoxFHUPxlLMjaKLww--
Request Method:PUT
Status Code:403 Forbidden
Remote Address:52.216.165.77:443
Referrer Policy:no-referrer-when-downgrade
响应Headers
Access-Control-Allow-Methods:GET, PUT, POST, DELETE
Access-Control-Allow-Origin:*
Access-Control-Expose-Headers:ETag
Access-Control-Max-Age:3000
Connection:close
Content-Type:application/xml
Date:Sun, 10 Dec 2017 10:53:12 GMT
Server:AmazonS3
Transfer-Encoding:chunked
Vary:Origin, Access-Control-Request-Headers, Access-Control-Request-Method
x-amz-id-2:0Mzo7b/qj0r5Is7aJIIJ/U2VxTTulWsjl5kJpTnEhy/B0fQDlRuANcursnxI71LA16AdePVSc/s=
x-amz-request-id:DA008A5116E0058F
请求Headers
Accept:*/*
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.9
Authorization:AWS ASIAJAR5KXKAOPTC64PQ:Wo9lbflZuVVS9+UTTDSjU0iPUbI=
Cache-Control:no-cache
Connection:keep-alive
Content-Length:1314943
Content-Type:application/octet-stream
DNT:1
Host:s3.amazonaws.com
Origin:http://132.12.23.145:8080
Pragma:no-cache
Referer:http://132.12.23.145:8080/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
X-Amz-Date:Sun, 10 Dec 2017 10:53:09 GMT
x-amz-security-token:FQoDYXdzENT//////////wEaDK9srK2+5FN91W+T+SLSA/LdEwpOiY7wDkgggOMhuGEiqIXAQrFMk/EqvZFl8Npqx414WsL9E310rj5mU1RGXsxuN+ers1r6NVPpJIlXSDG7bnwlGabejNvDL9vMX5HJHGbZOEVUoaL60/T5NM+0TZtH61vHAEVmRVFKOB0tSez8TEU1jQ2cJME0THn5RuV/6CuIpA9dlEYO7/ajB5UKT3F1rBkt12b0DeWmKG2pvTJRwa8nrsF6Hk6dk1B1Hl1fUwAh9rD17O9Roi7MFLKisPH+96WX08liC8k+n+kPPOox6ZZM/lOMwlNinDjLc2iC+JD/6uxyAGpNbQ7OHAUsF7DOiMvw6Nv6PrImrBvnK439BhLOk1VXCfxxmtTWGim8TD1w1EciZcJhsuCMpDF8fMnhF/JFw3KNOJXHUtpTGRjNbOPcPojVs3FgIt+9MllIA0pGMr2bYmA3HvKewnhD2qeKkG3DPDIbpwuRoY4wIXCP5OclmoHp5nE5O94aRIvkBvS1YmqDQO+jTiI7/O7vlX63q9sGqdIA4nwzh5ASTRJhC2rKgxepFirEB53dCev8i9f1pwXG3/4H3TvPCLVpK94S7/csNJexJP75bPBpo4nDeIbOBKKIMuUDK1pQsyuGwuUolKS00QU=
X-Amz-User-Agent:aws-sdk-js/2.164.0 callback
查询字符串参数
partNumber:2
uploadId:ljaviv9n25aRKwc4HKGhBbbXTWI3wSGZwRRi39fPSEvU2dcM9G7gO6iu5w7va._dMTZil4e_b53Iy5ngojJqRr5F6Uo_ZXuF27yaqizeARmUVf5ZVeah8ZjYwkZV8C0i3rhluYoxFHUPxlLMjaKLww--
实际响应Body
这是来自 S3 的响应的 body:
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>8277A4969E955274</RequestId><HostId>XtQ2Ezv0Wa81Rm2jymB5ZwTe+OHfwTcnNapYMgceqZCJeb75YwOa1AZZ5/10CAeVgmfeP0BFXnM=</HostId></Error>
问题
- 这显然不是
sts.generateFederationToken()
请求创建的信用的问题,因为如果它是那么较小的 (non-multipart) 上传也会失败,对吗? cdn.myapp.com
存储桶上的 CORS 配置显然不是问题,因为如果是的话,较小的 (non-multipart) 上传也会失败,对吗?- 为什么 S3 会接受分段上传的
partNumber=1
,然后在同一上传的partNumber=2
上接受 403?
一个解决方案
经过几个小时的努力,我发现问题出在我作为 Policy
我的 AWS.STS.getFederationToken()
请求的参数。具体来说,AWS.S3.upload()
只为第一个PUT
请求发送一个x-amz-acl
header,也就是调用S3.initiateMultipartUpoad
.
x-amz-acl
header 未 包含在后续 PUT
上传实际部分的请求中。
我的 IAM 策略 有以下条件,我用它来确保任何上传必须具有 'private' 的 ACL:
Condition : {
StringEquals : {
's3:x-amz-acl' : ['private']
}
}
所以对 S3.initiateMultipartUpload
的初始 PUT
请求没问题,但随后的 PUT
失败了,因为他们没有 x-amz-acl
header .
解决方案是编辑我附加到临时用户的策略并将 s3:PutObject
权限移动到它自己的语句中,然后调整条件以仅在目标值存在时应用。最终政策如下所示:
var policy = {
Version : '2012-10-17',
Statement : [
{
Effect : 'Allow',
Action : [
's3:PutObject'
],
Resource : [
'arn:aws:s3:::' + bucket + '/' + account._id + '/files/' + file.name
],
Condition : {
StringEqualsIfExists : {
's3:x-amz-acl' : ['private']
}
}
},
{
Effect : 'Allow',
Action : [
's3:AbortMultipartUpload'
],
Resource : [
'arn:aws:s3:::' + bucket + '/' + account._id + '/files/' + file.name
]
}
]
};
希望这能帮助其他人避免在这上面浪费三天时间。