I want to store images in an S3 bucket and then download the images via an http request using AWS's SDK for nodejs.
Here's my set up
// aws.js
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
const config = ({
region: 'us-east-2',
credentials: {
accessKeyId: "ACCESS_KEY",
secretAccessKey: "SECRET_ACCESS_KEY"
}
})
const s3Client = new S3Client(config);
const bucketParams = {
Bucket: 'aws-bucket',
Key: 'image1.jpg'
}
const getS3 = async () => {
try {
const data = await s3Client.send(new GetObjectCommand(bucketParams));
return await data.Body.transformToString();
} catch (err) {
console.log('err:', err);
}
}
getS3()
.then((res) => {
console.log('res:', res)
})
.catch((err) => {
console.log('err:', err);
});
When I send the request I expect to see a url, or a url in an object printed to the console like this:
res: "https://test-bucket-from-nodejs-sdk.s3.us-east-2.amazonaws.com/image1.jpg"
// or
res: ["https://test-bucket-from-nodejs-sdk.s3.us-east-2.amazonaws.com/image1.jpg"]
This is what gets printed to the console
res: �����%��~ Z�����o�~����!������8~�_�O|&��.��=wG��M�f�'C6Ww3�1��H�t�l(f�{)�G�0���~Ο���?�[�'���;�t��|=��F�Ƌ&��ƭ$�鬓Γyq)���� �@?d���F����amw᎗����W���\����4�:��PI��¤W��_�H��O* %�F���Dg���%�oŽw�m��u�{=N��(|I=�CCԗP�q
۸�J ���
���A҆��$���q�sx��L8K�\*�cߊ���O��6�.��_
s���������w���?���|W�S��.�R49 �S���%�o��4�O<]&7Eq�N���~��_�ટ�����|n�|k������ׅ�/�>�
N<2� �9H@�x���N�
Y�x3X�{��K��y<]�B�_��<7���I����1Ln��I-�E��K
��
*[���dN�SE|����^��|5⋋/k>
��mWP�u(`��c5��մ�K�R�q
�����w~ֿ
>"��⯀�[��_5(�|C��¢�� }F�YU�g����r�)U�B� OQ_���U|u�|S�Ś?�t�M:�U���O.-[Q��P���nKFm�۱,�b��]dT�<-�9��|W�v��徛��L|y�|5մ
�8����Zƥk�Em�[��4X���i$�Y���K�
I think it's because image files aren't readable in a node server environment, which might explain the replacement chars.
Does that I mean I need some kind of middleware like a buffer to make the image file readable?
- UPDATE
I've resolved half the problem...
I was using the wrong S3Client method. What I should've been using was ListObjectsV2Command
instead of GetObjectCommand
.
updated code:
///aws.js
const { S3Client, ListObjectsV2Command } = require('@aws-sdk/client-s3');
const s3Client = new S3Client(config);
const getS3List = async () => {
const command = new ListObjectsV2Command({Bucket:'test-bucket-from-nodejs-sdk'});
const response = await s3Client.send(command);
return response;
}
getS3List()
.then((data) => {
console.log('data:', data);
})
This returns a json object with a list of the object keys in the s3 bucket
// bash window
node aws.js
data: {
'$metadata': {
httpStatusCode: 200,
requestId: 'FH5WZY9VXGGQ5BWC',
extendedRequestId: 'pCMCEpPggwNka9Sj/jWZpFo8HmT/j2gd2B93i/COf8v3clgBWfK1Zw5tK3jbChOEuqKd756vRes=',
cfId: undefined,
attempts: 1,
totalRetryDelay: 0
},
Contents: [
{
Key: 'helloWorld.txt',
LastModified: 2022-12-12T01:13:23.000Z,
ETag: '"5eb63bbbe01eeed093cb22bb8f5acdc3"',
ChecksumAlgorithm: undefined,
Size: 11,
StorageClass: 'STANDARD',
Owner: undefined
},
{
Key: 'image1.jpg',
LastModified: 2022-12-12T01:09:41.000Z,
ETag: '"8b82adec06401f845e9564b05d3908dd"',
ChecksumAlgorithm: undefined,
Size: 192757,
StorageClass: 'STANDARD',
Owner: undefined
}
],
IsTruncated: false,
KeyCount: 2,
MaxKeys: 1000,
Name: 'test-bucket-from-nodejs-sdk',
Prefix: ''
}
NOW the issue is making a working url to map over an image tag using the Keys, I've tried using the Object URL and s3 URI addresses as src attributes and neither one work.
// server.js
app.get('/test', (req, res) => {
getS3List()
.then((data) => {
console.log(data.Contents[1].Key)
res.send(`<img src='https://test-bucket-from-nodejs-sdk.s3.us-east-2.amazonaws.com/${data.Contents[1].Key}'/>`)
})
.catch((err) => {
throw err;
})
})
Any thoughts?
I'm expecting the S3 server to return the url of the image stored in the bucket that I can use as the src for an html tag. The when I call the /test endpoint I want the tag with the s3 image url used a a source to be returned and rendered on the browser window
ex: expected behavior
// app.js
app.get('/test', (req, res) => {
getS3List()
.then((data) => {
console.log(data.Contents[1].Key)
res.send(`<img src=${'https://res.cloudinary.com/darp0mj9i/image/upload/v1670551774/fabio/theater/Dario_Fo_s_theatrical_show_G._Polidori_designer_uwv3mf.jpg'}></img>`)
})
.catch((err) => {
throw err;
})
})
renders this
But this is what ends up happending when I use the s3 object url
app.get('/test', (req, res) => {
getS3List()
.then((data) => {
console.log(data.Contents[1].Key)
res.send(`<img src='https://test-bucket-from-nodejs-sdk.s3.us-east-2.amazonaws.com/${data.Contents[1].Key}'></img>`)
})
.catch((err) => {
throw err;
})
})
Which means it's not a valid url
CodePudding user response:
In order to get access to an object's url stored in an AWS S3 bucket, you either need to enable public read access or create a pre-signed url which will give temporary access.
Either way, the code is behaving as expected now. It's return a rendered image on the broswer
- working solution:
// aws.js
const { S3Client, ListObjectsV2Command } = require('@aws-sdk/client-s3');
const config = ({
region: 'us-east-2',
credentials: {
accessKeyId: "ACCESS_KEY",
secretAccessKey: "SECRET_ACCESS_KEY"
}
})
const s3Client = new S3Client(config);
const getS3List = async () => {
const command = new ListObjectsV2Command({Bucket:'test-bucket-from-nodejs-sdk'});
const response = await s3Client.send(command);
return response;
}
module.exports = { getS3List }
// server.js
const express = require('express');
const app = express();
app.use(express.json());
const { getS3List} = require('./aws.js');
app.get('/test', (req, res) => {
getS3List()
.then((data) => {
let image = data.Contents[1].Key;
console.log('image:', image);
res.send(`<img src='https://test-bucket-from-nodejs-sdk.s3.us-east-2.amazonaws.com/${image}'></img>`)
})
.catch((err) => {
throw err;
})
})
- to enable public access https://aws.amazon.com/premiumsupport/knowledge-center/read-access-objects-s3-bucket/