Downloading S3 Objects Using Presigned URLs

Downloading S3 Objects Using Presigned URLs


For my current work project I am generating an Excel workbook and saving it to a private S3 bucket. I have a UI built in React that should allow that Excel file to be downloaded with just a click of a button. At first I thought it had to be complicated: expose an API Gateway endpoint that connects to an AWS Lambda that queries S3 for that object and sends that over by a bytestream. Ugh. One thing though was Excel sheets didn’t translate well to bytestream (or at least it wasn’t obvious how to do it with the library I was working with).

…And then I discovered: presigned URLs


With these babies you can generate a somewhat secure temporary link that exposes your S3 file as a downloadable link for the amount of time you set that link’s lifetime to.

As usual, Boto3 documentation on presigned URLs in Python could have been better.

But then...

Huzzah I found a worthy example of usage.

Dynamically Generated Download Link

import logging
import boto3
from botocore.exceptions import ClientError

def create_presigned_url(bucket_name, object_name, expiration=3600)
    # Generate a presigned URL for the S3 object
    s3_client = boto3.client('s3')
        response = s3_client.generate_presigned_url('get_object',
                                                    Params={'Bucket': bucket_name,
                                                            'Key': object_name},
    except ClientError as e:
        return None

    # The response contains the presigned URL
    return response

Now all I had to do was return the link as a response to when data is POST-ed on the frontend and voila, an easy to use and maintain way to make downloads from private S3 possible!

React Client Side Snippet

const DownloadThing = () => {
const [downloadLink, setDownloadLink] = useState('')
const handleDownload = async (e) => {
    try {
     // endpoint points to Lambda that generates workbook and presigned URL
      const res = await fetch(process.env.REACT_APP_API_ENDPOINT, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        body: JSON.stringify(data)

      // URL returned from my POST call
      const { downloadurl } = await res.json()
      alert(`Created Excel sheet! ${downloadurl}`)
    catch (err) {
return (
    <a href={downloadLink} download="test.xlsx">Get From S3</a>