My project involves developing a react native application similar to Slack, and I'm currently facing an issue with image uploads to S3.
I decided to use the getSignedUrl route for this functionality.
The process goes as follows: the client selects a photo, fetches a signed URL from the bucket, then updates the URL on the server for that specific user. Finally, a PUT request is made to the signed URL that was obtained.
Most of the time, everything works smoothly - the files end up in the correct bucket, and they are indeed photos. However, there are two main issues:
A) When clicking the link, the file gets downloaded instead of being displayed in the browser.
B) The file turns out to be an XML file rather than an image, and can only be opened in software like Photoshop.
I've attempted various solutions such as adjusting the data.append type, adding headers to the signed request, including x-amz- headers, hard-coding the file type on the server, converting the image to a base64 string using a native module, but the problem persists.
On the client-side, calls are made to the server:
uploadToServer() {
// alert('coming soon!');
//Go back to profile page
this.props.navigation.goBack();
//grab user from navigator params
let user = this.props.navigation.state.params.user
let pic = this.state.selected;
// turn uri into base64
NativeModules.ReadImageData.readImage(pic.uri, (image) => {
console.log(image);
var data = new FormData();
data.append('picture', {
uri: image,
name: pic.filename,
type: 'image/jpeg'
});
//get the signed Url for uploading
axios.post(api.getPhotoUrl, {fileName: `${pic.filename}`}).then((res) => {
console.log("get Photo URL response", res);
//update the user with the new url
axios.patch(api.fetchUserByID(user.id), {profileUrl: res.data.url}).then((resp) => {
console.log("Update User response", resp.data);
}).catch(err => errorHandler(err));
//upload the photo using the signed request url given to me.
//DO I NEED TO TURN DATA INTO A BLOB?
fetch(res.data.signedRequest, {
method: 'PUT',
body: data
}).then((response) => {
console.log("UPLOAD PHOTO RESPONSE: ", response);
}).catch(err => errorHandler(err))
}).catch((err) => errorHandler(err))
})
}
The logic for obtaining the signed URL unfolds as follows:
router.post('/users/sign-s3', (req, res) => {
const s3 = new aws.S3({signatureVersion: 'v4', region: 'us-east-2'});
const fileName = `${req.user.id}-${req.body.fileName}`;
const fileType = req.body.fileType;
const s3Params = {
Bucket: AWS_S3_BUCKET,
Key: `images/${fileName}`,
Expires: 60,
ContentType: 'image/jpeg',
ACL: 'public-read'
};
s3.getSignedUrl('putObject', s3Params, (err, data) => {
if (err) {
console.log(err);
return res.end();
}
const returnData = {
signedRequest: data,
url: `https://${AWS_S3_BUCKET}.s3.amazonaws.com/${s3Params.Key}`
};
res.write(JSON.stringify(returnData));
res.end();
return null;
});
});