我的node.js express应用程序当前可以通过服务器将图像文件上传到S3存储桶,而不会出现问题。 aws.s3的所有配置都很好,我可以确认这项工作。我现在正在尝试实现从浏览器直接上传到S3存储桶的过程,并且在实现过程中遇到了一些问题(例如CORS等),我已经解决了所有这些问题。不幸的是,我似乎无法解决(我希望这是最终的问题),“我们计算出的请求签名与您提供的签名不匹配。请检查您的密钥和签名方法。”,错误代码“ SignatureDoesNotMatch”。
我仔细检查了我能找到的所有StackOverflow和Google结果,并尝试了建议的每个选项(在此过多提及),但无济于事。我相信我的代码尽可能干净和正确,但是希望您能获得解决此问题的任何指导。在本地主机上运行和运行在Heroku上托管的已部署网站时,此操作均失败。提醒一下,通过服务器上传的图像在两种情况下均能正常工作。
让我们从存储桶上的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>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
要获取signedUrl的服务器端代码:
const aws = require('aws-sdk');
const AWS_S3_BUCKET = process.env.AWS_S3_BUCKET;
aws.config.update({
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
region: 'eu-west-2'
});
const s3 = new aws.S3({signatureVersion: 'v4'});
exports.getSignS3 = (req, res, next) => {
const fileName = req.query.filename;
const fileType = req.query.filetype;
const s3Params = {
Bucket: AWS_S3_BUCKET,
Key: fileName,
Expires: 60,
ContentType: fileType,
ACL: 'public-read'
};
s3.getSignedUrl('putObject', s3Params, (err, data) => {
if(err){
return res.end();
}
const returnData = {
signedRequest: data,
url: `https://${AWS_S3_BUCKET}.s3.eu-west-2.amazonaws.com/${fileName}`
};
res.status(200).json(returnData);
});
};
最后是客户端脚本:
document.getElementById("file-input").onchange = () => {
const files = document.getElementById('file-input').files;
const file = files[0];
if(file == null){
return alert('No file selected.');
}
getSignedRequest(file);
};
function getSignedRequest(file){
const xhr = new XMLHttpRequest();
xhr.open('GET', `/image/sign-s3?filename=${encodeURIComponent(file.name)}&filetype=${file.type}`);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
const response = JSON.parse(xhr.responseText);
uploadFile(file, response.signedRequest, response.url);
}
else{
alert('Could not get signed URL.');
}
}
};
xhr.send();
}
function uploadFile(file, signedRequest, url){
const xhr = new XMLHttpRequest();
xhr.open('PUT', signedRequest);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
document.getElementById('preview').src = url;
document.getElementById('avatar-url').value = url;
}
else{
alert('Could not upload file.');
}
}
};
xhr.send(file);
}
预期结果是,将有一个GET请求转到服务器代码以调用AWS并获取签名的URL。然后,使用signedUrl向AWS发出一个OPTIONS请求,以返回有效选项。然后最后应该有一个PUT请求,使用signedUrl将映像推送到AWS。不幸的是,这失败了。
响应如下。我已经手动替换了我的访问密钥,并且存储区名称是以下片段。但是请确认这两个都是正确的。
<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>{myIamAwsAccessKeyId}</AWSAccessKeyId>
<StringToSign>
AWS4-HMAC-SHA256 20190511T105642Z 20190511/eu-west-2/s3/aws4_request 4046216f89b127260feadd407160c8e2fc3dd3a5d19096850c7e292f1c884bbf
</StringToSign>
<SignatureProvided>
f787cc83f3d4152d9682e5670ba725b65e095696649ca2f1eafb0da3513af93e
</SignatureProvided>
<StringToSignBytes>{removed for brevity}</StringToSignBytes>
<CanonicalRequest>
GET /IMG_5279.jpeg Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-
HMAC-SHA256&X-Amz-Credential={myIamAwsAccessKeyId}%2F20190511%2Feu-
west-2%2Fs3%2Faws4_request&X-Amz-Date=20190511T105642Z&X-Amz-
Expires=60&X-Amz-SignedHeaders=host%3Bx-amz-acl&x-amz-acl=public-
read host:{myBucketName}.s3.eu-west-2.amazonaws.com x-amz-
acl:public-read host;x-amz-acl UNSIGNED-PAYLOAD
</CanonicalRequest>
<CanonicalRequestBytes>{removed for brevity}</CanonicalRequestBytes>
<RequestId>05FBF12EBB2816D5</RequestId>
<HostId>angoSNMus7AiVIjCtknMCmtTPAJdc+RJ/I7IuTgwPNFiCOa9NgcdCb938bTHkyN2bXlTxfk/5NU=</HostId>
</Error>
任何解决此问题的建议都将不胜感激。