Articles like this one from heroku show it's possible to sign a request in one's Node server and upload large files directly to S3 from the client, but I haven't been able to find a similar solution for downloading large files directly from S3 to the client.
More info on what I'm trying to do below:
I'm building a React web app with a Node js server hosted on Heroku. Users create and edit audio files that are then stored in an S3 bucket.
In following sessions, the audio must be loaded from the S3 bucket into the user's browser once again. To do this, the web app sends a request to the node js server that then retrieves the file and sends it to the client with the following endpoint:
// LOAD AUDIO
router.get("/getaudio/:userEmail/:projectId/:sectionId", async (req, res) => {
//create key from user/project/section IDs
const userEmail = req.params.userEmail;
const projectId = req.params.projectId;
const sectionId = req.params.sectionId;
const key = `${userEmail}/${projectId}/${sectionId}.webm`;
const params = {
Key: key,
Bucket: s3BucketName
}
s3.getObject(params, function (error, data) {
if (error) {
console.error(error);
}
res.send(data);
});
});
The problem here is that the audio files can be really large (up to 1.5 hours long). Loading these files into the Node js server and then sending them to the client feels really inefficient and can lead to unnecessary load on my Heroku dyno, as well as terribly long wait times for users.
However, I don't want to use the AWS SDK directly in my client (cutting out the server altogether) because I've been told this is bad practice and can expose your environment variables.
Is there a way to download large files from my s3 bucket directly to the React client safely and securely?
CodePudding user response:
You can generate a presigned URL on your node.js server that grants the access to the s3 object, send that URL to your React app and download the file directly from the React app.
It is important to note that the presigned URL expires in 15 minutes by default. So, if you need to increase the expiration time, you can specify how long (in seconds) your URL stays valid for by passing the Expires
parameter.
Your node.js handler may look something like as follows:
router.get("/getaudio/:userEmail/:projectId/:sectionId", async (req, res, next) => {
//create key from user/project/section IDs
const userEmail = req.params.userEmail;
const projectId = req.params.projectId;
const sectionId = req.params.sectionId;
const key = `${userEmail}/${projectId}/${sectionId}.webm`;
const params = {
Key: key,
Bucket: s3BucketName
}
s3.getSignedUrl('getObject', params, function (err, url) {
if (err) {
console.error(err)
next(err);
} else {
res.json({ url })
}
})
});