Reputation: 1
I am pretty new to using AWS S3, and am trying to upload an image to S3, but am running into the infamous SignatureDoesNotMatch error. I made sure of any typos: (ie. leading, trailing spaces in my keys), checked if my system time was off, but all to no avail. My workflow goes as follows (which is mostly taken from a tutorial if you want to reference: https://medium.com/@diego.f.rodriguezh/direct-image-upload-to-aws-s3-with-react-and-express-2f063bc15430). I tried following all of the previous posts on this issue but I still can't solve it.
First, in a queries.js I handle the signing of the URL:
accessKeyId: process.env.S3_KEY, // stored in the .env file secretAccessKey: process.env.S3_SECRET, // stored in the .env file region: process.env.BUCKET_REGION // This refers to your bucket configuration. }); // Creating a S3 instance const s3 = new AWS.S3(); // Retrieving the bucket name from env variable const Bucket = process.env.BUCKET_NAME; // PUT URL Generator function generatePutUrl(Key, ContentType) { return new Promise((resolve, reject) => { // Note Bucket is retrieved from the env variable above. console.log('the key is:', Key) const params = { Bucket, ContentType, Expires:3600, Key }; // Note operation in this case is putObject s3.getSignedUrl('putObject', params, function(err, url) { if (err) { console.log(err) reject(err); } // If there is no errors we can send back the pre-signed PUT URL resolve(url); }); }); }
Then in index.js:
// PUT URL
app.get('/generate-put-url', (req,res)=>{
// Both Key and ContentType are defined in the client side.
// Key refers to the remote name of the file.
// ContentType refers to the MIME content type, in this case image/jpeg
const { Key, ContentType } = req.query;
generatePutUrl(Key, ContentType).then(putURL => {
console.log({putURL})
res.send({putURL});
})
.catch(err => {
res.send(err);
});
});
app.listen(port, () => {
console.log(`Listening on port ${port}`);
});
And then my component uploader.js that really renders the upload button where I make the get request for the signed url, which I then return to make the put request to S3:
getImage = e => {
const files = e.target.files;
if (files && files.length > 0) {
const file = files[0];
this.setState({ file });
}
};
uploadFile = e => {
e.preventDefault();
const { file } = this.state;
this.setState({message:'Uploading...'})
const contentType = file.type; // eg. image/jpeg or image/svg+xml
console.log(contentType)
const generatePutUrl = 'http://localhost:4000/generate-put-url';
const options = {
params: {
Key: file.name,
ContentType: contentType,
},
headers: {
'Content-Type': contentType
}
};
axios.get(generatePutUrl, options).then(res => {
const {
data: { putURL }
} = res;
axios
.put(putURL, file, options)
.then(res => {
this.setState({message:'Upload Successful'})
setTimeout(()=>{
this.setState({message:''});
document.querySelector('#upload-image').value='';
}, 2000)
})
.catch(err => {
this.setState({message:'Sorry, something went wrong'})
console.log('err', err);
});
});
};
For reference, I logged my signed URL before it entered the put request, and it looked like this (with access key x-ed out)
putURL: 'https://polici-media.s3.us-east-2.amazonaws.com/32bitadder.JPG?Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIXXXXXXXXXXX%2F20200627%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200627T050401Z&X-Amz-Expires=3600&X-Amz-Signature=d12f6228e20d3bc5fab2b38bd6e250992f3e362418eb8f61ed6c886a71eb46e7&X-Amz-SignedHeaders=host'
For those that might be following along in the tutorial, all I really changed was from using port 3500 to port 4000 for my backend, but that isn't an issue in this case. I also put this URL in postman to no avail, though the URL https://polici-media.s3.us-east-2.amazonaws.com/32bitadder.JPG with the proper headers and authentication works just fine on postman
Upvotes: 0
Views: 1312
Reputation: 1332
I solved this by removing the Content-Type
from the headers.
Upvotes: 0