2024 Response - With AWS SDK 3.0
Step 1: The Necessary Packages:
The new SKD split the functionalities into multiple smaller packages. To begin, you need @aws-sdk/client-s3 and probably the @aws-sdk/s3-request-presigner, in case you want to have a temporary link to download.
yarn add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
One of the advantages of AWS SDK 3.0 is the inclusion of typeScript files (.d.ts), which means you don't have to worry about it. This makes the transition to the new version even easier.
Step 2: Credentials
This is one of the concepts that changed compared to the old AWS SDK. First, I recommend you look at this documentation, which presents the critical differences between v2 and v3 regarding credentials.
This StackOverflow question teaches how to create the credentials well. In short, you need this:
region: 'your-region', credentials: { accessKeyId: 'Create in IAM, your-access-key - save in your .env', secretAccessKey: 'Create in IAM, save securely' }
Step 3. Initiate the Client, Setup the Command
The parameters and the packages are always the same; only the parameters and the packages change. For S3, this is what you must do:
import { S3Client, GetObjectCommand, HeadObjectCommand } from '@aws-sdk/client-s3'; import { IAwsCredentials } from 'interfaces'; const s3Client = (awsCredentials: IAwsCredentials) => new S3Client({ region: awsCredentials.region, credentials: { accessKeyId: awsCredentials.credentials.access_key_id, secretAccessKey: awsCredentials.credentials.secret_access_key } }); export const AwsConnectorTool = { checkIfExists: async (path: string, awsCredentials: IAwsCredentials): Promise<boolean> => { const bucketName = process.env.AWS_STORAGE_BUCKET_NAME; const command = new HeadObjectCommand({ Bucket: bucketName, Key: path }); try { await s3Client(awsCredentials).send(command); return true; } catch { return false; } }, downloadFromS3: async ( key: string, awsCredentials: IAwsCredentials ): Promise<any | undefined> => { const bucketName = process.env.AWS_STORAGE_BUCKET_NAME!; const command = new GetObjectCommand({ Bucket: bucketName, Key: key // key is the file path you want to download. Check image below }); // check the links below to understand why I am returning the Stream and not the Buffer return (await s3Client(awsCredentials).send(command)).Body; } }
This is the Key:

Step 4. ReadableStream instead of Buffer
I had issues using a Buffer with React. If you are facing a similar challenge, prefer returning the Readable Stream straight away with the command return (await s3Client(awsCredentials).send(command)).Body;. This will allow you to access it as a blob, making your life easier.
Read this other answer to understand it in depth: https://stackoverflow.com/a/78587519/5936119.
There, I added all the documentation about Streaming and how to download from ReadableStreaming into an audio tag. But in short, this is what you are looking for:
import { useEffect, useState } from 'react'; import axios from 'axios'; const downloadBlob = async (url: string, data: any, token: string) => { const axiosInstance = axios.create({ headers: { Authorization: `Bearer ${token}` // remove if you don't need }, responseType: 'blob' // this is important }); return await axiosInstance.post(url, data); }; (...) const response = await downloadBlob(url, data); const blob = (await response).data; setMediaBlob(blob); useEffect(() => { if (mediaBlob?.size) { const url = URL.createObjectURL(mediaBlob); setAudioUrl(url); return () => { URL.revokeObjectURL(url); }; } }, [mediaBlob?.size]); (...) return ( // ... <audio preload='metadata' controls> {audioUrl && <source src={audioUrl} type='audio/mpeg' />} Your browser does not support the audio element. </audio> ) };
Using NodeJS, Express, AWS3 S3 SDK, React, Axios, and TypeScript requires no extra components. You can also replace axios with fetch if you prefer.
NodeJS Streaming Documentation: https://nodejs.org/api/stream.html#readablepipedestination-options