【发布时间】:2020-05-15 06:49:57
【问题描述】:
我正在尝试使用 AWS S3 预签名 URL 为 Vue 应用程序实现图片上传功能。第一步是向 API 发送请求,该 API 将创建签名 URL 以上传文件。这部分工作正常:
服务器端:
'use strict';
const aws = require('aws-sdk');
const config = require('../config');
const util = require('./util');
const uuidv4 = require('uuid/v4');
const bucketName = 'myAmazonS3Bucket';
aws.config.update({
secretAccessKey: config.AWS_SECRET_ACCESS_KEY,
accessKeyId: config.AWS_ACCESS_KEY_ID,
region: 'us-west-2'
});
const s3 = new aws.S3({ signatureVersion: 'v4' });
const handler = async (event) => {
console.log('Uploading file...');
return await getUploadURL();
}
const getUploadURL = async () => {
const actionId = uuidv4();
const s3Params = {
Bucket: bucketName,
Key: `${actionId}.jpg`,
ContentType: 'image/jpeg',
ACL: 'public-read'
};
console.log(s3Params);
return new Promise((resolve, reject) => {
let uploadURL = s3.getSignedUrl('putObject', s3Params);
console.log(uploadURL);
resolve({
"statusCode": 200,
"isBase64Encoded": false,
"headers": { "Access-Control-Allow-Origin": "*" },
"body": JSON.stringify({
"uploadURL": uploadURL,
"photoFilename": `${actionId}.jpg`
})
});
reject({
"statusCode": 500,
"headers": { "Access-Control-Allow-Origin": "*" },
"body": "A funky error occurred and I am not happy about it!"
})
});
}
module.exports = {
handler
}
API 端点发送一个类似下面的响应:
{
"uploadURL": "https://s3.us-west-2.amazonaws.com/pics.amazon-clone.io/7925d452-cadd-4f06-ba63-cc50645e3cfb.jpg?AWSAccessKeyId=AKIASGDJJ5ZLUVPMUYMQ&Content-Type=image%2Fjpeg&Expires=1580276753&Signature=3rqNckP4DiL6DkWPRuEGJsuIGpw%3D&x-amz-acl=public-read",
"photoFilename": "7925d452-cadd-4f06-ba63-cc50645e3cfb.jpg"
}
客户端将使用uploadUrl 将文件上传到S3 存储桶。这是客户端代码:
uploadImage: async function (e) {
console.log('Upload clicked')
console.log(e)
// Get the presigned URL
const response = await axios({
method: 'POST',
url: API_ENDPOINT
})
console.log('Response: ', response.data)
console.log('Uploading: ', this.image)
let binary = atob(this.image.split(',')[1])
let array = []
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i))
}
let blobData = new Blob([new Uint8Array(array)], {type: 'image/jpeg'})
console.log('Uploading to: ', response.data.uploadURL)
const result = await fetch(response.data.uploadURL, {
method: 'PUT',
headers: {
'Content-Type': 'image/jpeg',
'x-amz-acl': 'public-read' },
body: blobData
})
console.log('Result: ', result)
// Final URL for the user doesn't need the query string params
this.uploadURL = response.data.uploadURL.split('?')[0]
}
很遗憾,我在使用签名 URL 时遇到了禁止的 403 错误。这是我从 Chrome 浏览器得到的控制台错误的结果:
访问获取 'https://s3.us-west-2.amazonaws.com/pics.amazon-clone.io/b1bdb5e3-7f64-49f7-b779-11b3f67317ee.jpg?Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIASGDJJ5ZLUVPMUYMQ%2F20200129%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20200129T165522Z&X-Amz-Expires=900&X-Amz-Signature=b230c9a40065585307e150655466bbab3d0d99aa43f8620377ab977eb1c7234c&X-Amz-SignedHeaders=host%3Bx-amz-acl&x-amz-acl=public-read' 来自原点“http://localhost:8080”已被 CORS 策略阻止: 对预检请求的响应未通过访问控制检查:否 请求中存在“Access-Control-Allow-Origin”标头 资源。如果不透明的响应满足您的需求,请设置请求的 模式为“no-cors”以获取禁用 CORS 的资源。
【问题讨论】:
-
如果这是一个 Lambda 函数,您应该强烈考虑利用 IAM 角色,而不是在使用 Lambda 函数打包和上传的配置文件中提供硬编码的 AWS 凭证。
-
大概您的目标 S3 存储桶允许使用公开读取的预设 ACL 上传对象。并且您的客户端在预签名 URL 过期之前以及您用于创建预签名 URL 的凭据过期之前使用它。
标签: amazon-web-services amazon-s3 pre-signed-url