I am following the guide here to try and upload files to S3.
This is successfully working for various filetypes (tested with .png, .jpg, .gif and .txt) but fails whenever trying to upload an svg file (ContentType: image/svg+xml).
The PUT request responds with _403 Forbidden_, with statusText _Error Forbidden_.
An example signedUrl is:
Notice that the ContentType is encoded as: Content-Type=image%2Fsvg%20xml (where %20 is a space, rather than a +)
It seems that it is encoding image/svg+xml as image/svg xml
I have tried manually replacing this with %2B instead but still receive the same error.
My node code is:
router.get('/sign-s3', (req, res, next) => {
const s3 = new AWS.S3();
const fileName = req.query['file-name'];
const fileType = req.query['file-type'];
const s3Params = {
Bucket: S3_BUCKET,
Key: fileName,
Expires: 60,
ContentType: fileType,
ACL: 'public-read'
};
s3.getSignedUrl('putObject', s3Params, (err, data) => {
if (err) {
console.log(err);
return res.end();
}
const returnData = {
signedRequest: data,
url: `https://${S3_BUCKET}.s3.amazonaws.com/${fileName}`
};
res.write(JSON.stringify(returnData));
res.end();
});
});
The client-side uploadFile operation is:
function uploadFile(file, signedRequest, url){
const xhr = new XMLHttpRequest();
// signedRequest = signedRequest.replace('svg%20', 'svg%2B')
xhr.open('PUT', signedRequest);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
console.log('new url');
console.log(url);
}
else{
console.log('Could not upload file.');
console.log("Error", xhr.statusText);
}
}
};
xhr.send(file);
}
Is uploading SVGs supported?
There is a post here of a maybe similar issue happening with the dot-net sdk: http://stackoverflow.com/questions/9051650/why-is-my-s3-pre-signed-request-invalid-when-i-set-a-response-header-override-th
As a side-note, I have noticed the official docs say that ContentType does not have to be provided as a param to the getSignedUrl operation, but that is not working for me in any case (for pngs, gifs, or jpgs for example). Is that example valid?
var params = {Bucket: 'myBucket', Key: 'myKey'};
var url = s3.getSignedUrl('putObject', params);
console.log("The URL is", url);
SDK version: 2.6.2
Node version: 6.5.0
@bhishp
Can you log what your s3Params looks like, minus the bucket name, when you are trying to upload an svg?
I recreated your sample (though just using http instead of express), and was able to create a valid url and upload with it.
// s3Params
{ Bucket: 'BUCKET',
Key: 'test.svg',
Expires: 60,
ContentType: 'image/svg+xml',
ACL: 'public-read' }
URL:
https://BUCKET.s3-us-west-2.amazonaws.com/test.svg?AWSAccessKeyId=ID&Content-Type=image%2Fsvg%2Bxml&Expires=1473874113&Signature=l57f1QQzt7Ilq%2BnYR%2F%2BsurQNRZg%3D&x-amz-acl=public-read
In my url, the + is encoded as %2B, not a space.
Can you also share which browser you are testing in?
@bhishp
As for your side-note, I can confirm you don't need to specify the ContentType when using signatureVersion: 'v4' with S3. I'll have to investigate if it can also work with signatureVersion: 'v2' (default when you don't specify a signatureVersion for your S3 client, and the region supports both versions), or update the docs if it can't.
ah.. I don't know how I didn't see it before. Yes, the problem is that I wasn't encoding the file-type when GETing /sign-s3.
Before:
xhr.open('GET', /api/sign-s3?file-name=${file.name}&file-type=${file.type});
Creating a HTTP Request like this (the + subsequently being interpreted as a space):
http://localhost:3000/api/sign-s3?file-name=circle.svg&file-type=image/svg+xml
After:
xhr.open('GET', /api/sign-s3?file-name=${encodeURIComponent(file.name)}&file-type=${encodeURIComponent(file.type)});
Creating a HTTP request with + correctly encoded:
http://localhost:3000/api/sign-s3?file-name=circle-with-header.svg&file-type=image%2Fsvg%2Bxml
Side-note
Additionally, I can confirm that omitting ContentType is valid now that I have set signatureVersion: 'v4'
@chrisradek Thanks for your help and quick response! :+1: :tada:
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread.
Most helpful comment
@bhishp
Can you log what your
s3Paramslooks like, minus the bucket name, when you are trying to upload an svg?I recreated your sample (though just using
httpinstead ofexpress), and was able to create a valid url and upload with it.URL:
In my url, the
+is encoded as%2B, not a space.Can you also share which browser you are testing in?