67

I was trying to download a file from a bucket on Amazon S3. I was wondering if I can write a javascript to download such a file from a bucket. I was googling it, but couldn't find any resources that can help me do that.

Some steps in mind are: authenticate Amazon S3, then by providing bucket name, and file(key), download or read the file so that I can be able to display the data in the file.

Thanks,

3
  • you can use the download attribute on an <A> tag to trigger a download instead of a navigation. Commented May 28, 2013 at 19:45
  • but it isn't that simple because we need to deal with the Amazon S3 credentials ? Commented May 28, 2013 at 19:48
  • 1
    as long as the user is "in", it will work just fine. cookies keep them logged in for a while, so you don't have to be on the an S3 html page for the link to work... if you need authentication help, that's really another question, but you need to answer it before you can roboticize the the download with javascript. Commented May 28, 2013 at 19:53

4 Answers 4

66

Maybe you can use AWS Node.js API:

var AWS = require('aws-sdk'); AWS.config.update( { accessKeyId: ".. your key ..", secretAccessKey: ".. your secret key ..", } ); var s3 = new AWS.S3(); s3.getObject( { Bucket: "my-bucket", Key: "my-picture.jpg" }, function (error, data) { if (error != null) { alert("Failed to retrieve an object: " + error); } else { alert("Loaded " + data.ContentLength + " bytes"); // do something with data.Body } } ); 
Sign up to request clarification or add additional context in comments.

8 Comments

Here is the direct link to s3.getObject in AWS Node.js API for the interested.
For anyone that wants to know, data.Body is the raw file and can be used in other modules like graphicsmagick: gm(data.Body).identify(function (err, value) {}); Pretty cool!
You can always save (download) the file like this in Node: fs.writeFile("mydata.tsv.gz", data.Body);
I surprised with alert in server side code on first eye :)
This example shows how to receive data from AWS S3 server. But how to save the data as a file from client browser? I have tried in function (error, data) { var blob = new Blob(data.Body, {type: 'application/octet-binary'}); var link = document.createElement('a'); var url = window.URL; if ( window.webkitURL ) { url = window.webkitURL; } link.href = url.createObjectURL(blob); link.download = fileName; link.click(); } But saves corrupted image file
|
52

I came here looking for away to download a s3 file on the client side. Here is how I solved it:

As, I can not store my s3 auth keys on client side, I used my server-side scripts to generate a pre-signed url and send it back to client like:

const AWS = require('aws-sdk') const s3 = new AWS.S3() AWS.config.update({accessKeyId: 'your access key', secretAccessKey: 'you secret key'}) const myBucket = 'bucket-name' const myKey = 'path/to/your/key/file.extension' const signedUrlExpireSeconds = 60 * 5 // your expiry time in seconds. const url = s3.getSignedUrl('getObject', { Bucket: myBucket, Key: myKey, Expires: signedUrlExpireSeconds }) // return the url to client 

Use this URL in the front-end to trigger download:

function download(url){ $('<iframe>', { id:'idown', src:url }).hide().appendTo('body').click(); } $("#downloadButton").click(function(){ $.ajax({ url: 'example.com/your_end_point', success: function(url){ download(url); } }) }); 

4 Comments

What exactly is a signed URL? Edit: TIL, advancedweb.hu/2018/10/30/s3_signed_urls
I found if I used the signed url in a new tab: window.open( signedUrl, '_blank' ); instead of the iframe, the browser could better handle mixed mime types in the downloaded files (binary, images, etc.), otherwise a very helpful answer.
You can read more about it here with a python example from aws : boto3.amazonaws.com/v1/documentation/api/latest/guide/…
@slh777 how can i use axios to download the file using that signedUrl.. ? Any ideas?
3

Other answers here work, but wanted to expand on what worked for me.

In my case, I was dealing with files too large for

function download(url){ $('<iframe>', { id:'idown', src:url }).hide().appendTo('body').click(); } 

to work. ( Was getting url is too long ) My solution was to include a hidden anchor tag, and trigger the click to that tag on ajax success. You can't use the anchor tag right off the bat unless you don't care about handling errors.

S3 will respond with an XML error file if something goes wrong, so the browser will automatically display that XML response. By first attempting to hit the URL with ajax, you can catch that error without showing the ugly XML. On success in that ajax call is when you know you're clear to try and download the file.

Comments

2

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:

AWS S3 Bucket 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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.