Show Image stored in s3 on Web page using flask

Viewed 908

Im trying to get images from an s3 bucket, and show them on a web page using flask (and boto3 to access the bucket).

I currently have a list of all the pictures from the bucket, but cant get the html to show them(gives me 404 error).

How do I do this without downloading the files? this is what I have so far:

def list_files(bucket):
    contents = []
    for image in bucket.objects.all():
        contents.append(image.key)
    return contents

def files():
    list_of_files = list_files(bucket)
    return render_template('index.html', my_bucket=bucket, list_of_files=list_of_files)

and this is the html snippet:

<table class="table table-striped">
        <br>
        <br>
        <tr>
          <th>My Photos</th>
           {% for f in list_of_files %}
            <td> <img src="{{ f }}"></td>

            {% endfor %}

Thanks a lot!

2 Answers

since loading an image to a html page requires a real image which exists in the directory. images from AWS S3 can be loaded onto a html page if you download them first in the directory, then use its url as a source in html <image> tag.

i found a solution to this but you need to modify it as your needs. define a function that loads the image from S3 as:

import matplotlib.image as mpimg
import numpy as np
import boto3
import tempfile

s3 = boto3.resource('s3', region_name='us-east-2')
bucket = s3.Bucket('bucketName')
object = bucket.Object('dir/subdir/2015/12/7/img01.jpg')
tmp = tempfile.NamedTemporaryFile()

def imageSource(bucket, object, tmp):
       with open(tmp.name, 'wb') as f:
            object.download_fileobj(f)
            src = tmp.name    #dir/subdir/2015/12/7/img01.jpg
            retrun src

Just ran into this problem as well, seems like this hasn't been updated for a while so will try to add it.

Your current approach below is right. The only issue is that in order to render an image that is not going to be downloaded to your server, you have to have a direct url to your S3 file. Currently, you only have the image name, not the full url.

def list_files(bucket):
contents = []
for image in bucket.objects.all():
    contents.append(image.key)
return contents

def files():
    list_of_files = list_files(bucket)
    return render_template('index.html', my_bucket=bucket, list_of_files=list_of_files)

Currently, your items in the list of files will look like this:

['file_name1', 'file_name2', 'file_name3']

In order for them to render in your browser directly you need them to look like this:

['file_url1', 'file_url2', 'file_url3']

s3 file urls look something like this: https://S3BUCKETNAME.s3.amazonaws.com/file_name1.jpg

Therefore, instead of the line below

contents.append(image.key)

you need to replace the image.key with something that makes the URL

contents.append(f'https://{S3BUCKETNAME}.s3.amazonaws.com/{image.key})

That should do it, the html you have should work correctly as is. The only other big risk is the files you uploaded are not public, for that you'll need to look at the settings of your bucket on AWS.

Additional Resources and Sources:

  1. Adding a public policy to your AWS S3 Bucket: https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html
  2. Uploading and downloading files with Flask & S3: https://stackabuse.com/file-management-with-aws-s3-python-and-flask/
Related