Richard Bridge
Richard Bridge

Reputation: 388

Using getObject from AWS to display an image in the browser

I'm using the @aws-sdk/client-s3 and I'm trying to read an image from a private bucket in AWS s3.

In my NextJS code, I have an API that should fetch the image and ultimately set the src tag of the <img> in the browser to display it. However I am not able to get it working and i'm unsure of the data I'm receiving back.

Here's the code to retrieve the image:

import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";

export const getObject = async () => {
    const s3Client = new S3Client({
        region: process.env.AWS_REGION,
        credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY,
            secretAccessKey: process.env.AWS_SECRET_KEY,
        },
    })
    console.log('GET OBJECT FUNC')
    try {
        // Create a helper function to convert a ReadableStream to a string.
        const streamToString = (stream) =>
            new Promise((resolve, reject) => {
                const chunks = [];
                stream.on("data", (chunk) => chunks.push(chunk));
                stream.on("error", reject);
                stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
            });

        // Get the object from the Amazon S3 bucket. It is returned as a ReadableStream.
        const data = await s3Client.send(new GetObjectCommand({
            Bucket: process.env.AWS_BUCKET,
            Delimiter: "/",
            Key: process.env.AWS_BUCKET_FOLDER + 'Screenshot from 2022-02-09 08-13-26.png'
        }));
        // return data; // For unit tests.
        // Convert the ReadableStream to a string.
        const bodyContents = await streamToString(data.Body);
        return bodyContents;
    } catch (err) {
        console.log("Error", err);
    }
};

export default async function handler(request, response) {
    if (request.method === "GET") {
        const data = await getObject()
        return response.status(200).json(data);
    } else {
        return response.status(405).end();
    }
}

Its just a screenshot I took and i'm trying to download again. When I console.log the data returned from the getObject function, It looks a like this:

enter image description here

Then on the frontend side, I have the following function that receives the data

    const getImage = async (event) => {
        console.log('GET IMAGE')
        // console.log(event.target)
        let arrayBuffer = await axios.get("/api/get-object", {responseType: 'arraybuffer'})
        console.log(arrayBuffer.data)
        let base64 = 'data:image/png;base64,' + Buffer.from(arrayBuffer.data).toString('base64')
        console.log(base64)
        event.target.src = base64
    }

And sets the src of the image. But the image isnt showing. The console.log output of the above function is:

GET IMAGE Asset.jsx:18:16
ArrayBuffer { byteLength: 687198 }
Asset.jsx:21:16
data:image/png;base64,Iu+/vVBOR1xyXG5cdTAwMWFcblx1MDAwMFx1MDAwMFx1MDAwMFxySUhEUlx1MDAwMFx1MDAwMFx1MDAwMixcdTAwMDBcdTAwMDBcdTAwMDIrXGJcdTAwMDZcdTAwMDBcdTAwMDBcdTAwMDDvv71y77+9UVx1MDAwMFx1MDAwMFx1MDAwMFx1MDAwNHNCSVRcYlxiXGJcYnxcYmTvv71cdTAwMDBcdTAwMDBcdTAwMDBcdTAwMTl0RVh0U29mdHdhcmVcdTAwMDBnbm9tZS1zY3JlZW5zaG9077+9XHUwMDAz77+9Plx1MDAwMFx1MDAwMFx1MDAwMCp0RVh0Q3JlYXRpb24gVGltZVx1MDAwMFdlZCAwOSBGZWIgMjAyMiAwODoxMzoyOSBHTVRcdTAwMTFcYllcdTAwMDRcdTAwMDBcdTAwMDAgXHUwMDAwSURBVHjvv73UvduS77+9Ou+/vSzvv71cdTAwMDHvv71S77+92rXvv73vv71M27zvv73vv71/77+9WO+/vV4p77+9QO+/vVx1MDAwNyBA77+9XHUwMDBiVGbvv71l77+9XHUwMDBm77+9ZCnvv70gXHUwMDEw77+9e1x1MDAwNC7vv73vv73vv73PvzlcdTAwMDB477+977+9Ke+/ve+/vWFmMDNs77+9XHUwMDEz77+9XHLvv71i77+9d++/ve+/vVBKXHUwMDAxXHUwMDAwLMuCbduQc0ZKXHTvv73vv71cdTAwMTPvv73vv73vv73vv71cdTAwMWRAKVx1MDAwNTnvv71277+9fT/vv71PKe+/ve+/vWvvv71cdTAwMDBcdTAwMDAz77+977+9fu+/ve+/ve+/vVnvv73vv70677+9Ne+/ve+/ve+/vVxuM8OyLHg+77+9XHUwMDAw77+977+9M++/ve+/vSc+Pj7vv73vv71w77+9UXfvv71377+964qcM++/vX3vv73vv71cdT…

All those slashes and /ve+ don't look correct to me, but I am at a loss as to what I'm doing wrong. I've tried about 5 different methods from all over stack overflow and I'm not able to get this image to show up. Can anyone help please.

Upvotes: 3

Views: 2968

Answers (2)

Ohadsh
Ohadsh

Reputation: 46

All you have to change is:

     ...
    // Get the object from the Amazon S3 bucket. It is returned as a ReadableStream.
    const data = await s3Client.send(new GetObjectCommand({
        Bucket: process.env.AWS_BUCKET,
        Delimiter: "/",
        Key: process.env.AWS_BUCKET_FOLDER + 'Screenshot from 2022-02-09 08-13-26.png'
    }));
    // return data; // For unit tests.
    // Convert the ReadableStream to a string.
    const bodyContents = await data.Body.transformToString('base64');
    ...

Upvotes: 0

Vladimir Szabo
Vladimir Szabo

Reputation: 458

I think your problem is here:

stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));

and here

let base64 = 'data:image/png;base64,' + Buffer.from(arrayBuffer.data).toString('base64')

You are converting the response string to UTF and then converting to base64 here. Change the AWS stream to base64 like this:

stream.on("end", () => resolve(Buffer.concat(chunks).toString("base64")));

and remove the decode when you use the image here:

let base64 = 'data:image/png;base64,' + arrayBuffer.data;

Upvotes: 3

Related Questions