I am using NodeJS backend API server that allows the frontend (ReactJS / React Native) to upload an image using form-data. This is the code I am using:
const s3 = new aws.S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: "us-east-1",
});
...
const upload = multer({
storage: multerS3({
s3,
bucket: process.env.AWS_BUCKET,
acl: 'private',
metadata(req, file, cb) {
cb(null, {fieldName: file.fieldname});
},
key(req, file, cb) {
cb(null, Date.now().toString() + '.png');
}
})
})
...
app.post('/upload', upload.single('photo'), (req, res, next) => {
res.json(req.file)
})
The API server works as it should and the image is uploaded in a private secured bucket using AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
.
However now I want to be able to render this image on ReactJS or React Native. Do you recommend building another NodeJS route called something like /render
and somehow stream it to the backend? If yes, how can I do it?
Alternatively, should I render it directly from the frontend and risking exposing the AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
keys to the user?
js file, using React Bootstrap as a dropdown menu to be able to select a particular file name: import React, { useState } from 'react'; import { ListGroup, Dropdown } from 'react-bootstrap'; import AWS from 'aws-sdk'; const InputDownload = () => { const [template, setTemplate] = useState('Choose Template'); AWS.
After a number of trial and errors, I found out that the most elegant solution is to have NodeJS to take care of the communication with S3. The frontend will then GET
the image from NodeJS through a route which can be done as follows:
app.get("/image/:imageId", function(req, res, next) {
var params = { Bucket: keys.AWS_BUCKET, Key: req.params.imageId };
s3.getObject(params, function(err, data) {
if (err) {
return res.send({ error: err });
}
res.send(data.Body);
});
});
Why do I think that this is more elegant than the solution proposed by luboskmac?
For starters, I am not exposing the asset to the public at no point in time. I am only revealing the NodeJS GET
route to the end user.
Secondly, if I wanted to restrict access to this route, I can simply use passportJS
inside NodeJS to, for example, restrict access to the asset to only logged in users. This is useful in many scenarios, such as in the case of GDPR requirements.
Solution is simple:
This way, users would be able to access only assets you send them from server + without AWS API keys (sending keys to browser would be huge security flaw)
This is very same concept e.g. Google Docs, Dropbox uses when you share a link and send it to somebody. Everybody in the world would be able to read that link, but if link is not guessable, asset can be accessed only if you share that link.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With