我正在我的应用程序中进行图像上传。我使用预签名的 POST URL 将文件上传到我的 S3 存储桶。负责创建 URL 的函数如下所示
public createUrl(filename: string): Promise<string> {
return new Promise((resolve, reject) => {
this.s3.createPresignedPost(
{
Bucket: "{{bucket-name}}",
Fields: {
key: filename,
},
Conditions: [
["content-length-range", 0, 1048576],
["starts-with", "$Content-Type", "image/"],
],
Expires: 120,
},
(error, data) => {
if (error) reject(error);
else resolve(data);
},
);
});
}
其中s3对象的创建如下
import { S3 } from "aws-sdk";
export const s3 = new S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});
当我使用
sls offline
命令测试所有内容时,一切似乎都工作正常,我得到以下响应
{
"url": "https://s3.{{region}}.amazonaws.com/{{bucket-name}}",
"fields": {
"key": "{{filename}}.png",
"bucket": "{{bucket-name}}",
"X-Amz-Algorithm": "AWS4-HMAC-SHA256",
"X-Amz-Credential": "AKIA{{key}}/20220619/eu-central-1/s3/aws4_request",
"X-Amz-Date": "20220619T131035Z",
"Policy": "{{policy-token}}",
"X-Amz-Signature": "{{signature}}"
}
}
然后,我可以使用所有这些值发出请求,将图像上传到我的存储桶,并且图像确实已上传。但是每当我使用
sls deploy
部署所有内容时,该函数生成的数据看起来完全正常,然后当我调用 s3 预签名 url 时,我会得到以下响应
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>InvalidAccessKeyId</Code>
<Message>The AWS Access Key Id you provided does not exist in our records.</Message>
<AWSAccessKeyId>ASIA{{key}}</AWSAccessKeyId>
<RequestId>{{id}}</RequestId>
<HostId>{{host-id}}</HostId>
</Error>
值得一提的是,当我使用
sls offline
时,我得到的密钥以 AKIA...
开头,但是当我调用已部署的版本时,我得到 ASIA...
。此外,AWS_ACCESS_KEY_ID
环境变量设置正确,因为我也使用 DynamoDB 客户端,并且它工作正常,我使用了完全相同的变量。
好吧,原来问题完全在我这边。我不知道为什么,但是当我改变线路时
export const s3 = new AWS.S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});
我没有使用环境变量,而是直接将这些值作为字符串,一切都神奇地开始工作。我不知道为什么会这样,因为我在设置 DynamoDB 客户端时确实使用了这些环境变量,它运行得很好。现在,我不再获得
ASIA...
键,而是获得 AKIA...
键,它们的作用就像魅力一样。
ASIA...
vs AKIA...
表明您正在使用 STS(使用假定角色生成的临时凭证)。这在 Lambda 中很常见,因为您不使用访问密钥,而是通过其 IAM 执行角色来承担角色。这意味着,对于每次调用,它都会生成有效期较短的临时凭证,并使用它们来执行所需的任何操作(在本例中为预签名 URL)。
实际从客户端下载/上传时,您需要在正文中发送“X-Amz-Security-Token”以及预签名时获得的值。不幸的是,AWS 没有明确记录这一点。
在 NodeJS 中,这将是:
const s3 = new AWS.S3();
const credentials = s3.config.credentials;
const sessionToken = credentials.sessionToken;
在 PHP 中是
$credentials = $s3client->getCredentials()->wait();
$sessionToken = $credentials->getSecurityToken();
上传/下载文件时,您可以将其作为正文发送:
"acl": presignUploadData.acl, // for upload
"key": presignUploadData.key, // the key you presigned for download/upload
"X-Amz-Credential": presignUploadData.XAmzCredential,
"X-Amz-Algorithm": presignUploadData.XAmzAlgorithm,
"X-Amz-Date": presignUploadData.XAmzDate,
"Policy": presignUploadData.Policy,
"X-Amz-Signature": presignUploadData.XAmzSignature,
"X-Amz-Security-Token": presignUploadData.XAmzSecurityToken,