Search code examples
pythonangularamazon-web-servicesionic2flask-restful

Storing user images on AWS


I'm implementing a simple app using ionic2, which calls an API built using Flask. When setting up the profile, I give the option to the users to upload their own images. I thought of storing them in an S3 bucket and serving them through CloudFront.

After some research I can only find information about:

  • Uploading images from the local storage using python.
  • Uploading images from a HTML file selector using javascript.

I can't find anything about how to deal with blobs/files when you have a front end interacting with an API. When I started researching the options I had thought of were:

  1. Post the file to Amazon on the client side and return the CloudFront url directly to the back end. I am not too keen on this one because it would involve having some kind of secret on the client side (maybe is not that dangerous, but I would rather have it on the back end).
  2. Upload the image to the server and somehow tell the back end about which file we want the back end to choose. I am not too keen on this approach either because the client would need to have knowledge about the server itself (not only the API).
  3. Encode the image (I have tought of base64, but with the lack of examples I think that it is plain wrong) and post it to back end, which will handle all the S3 upload/store CloudFront URL.

I feel like all these approaches are plain wrong, but I can't think (or find) what is the right way of doing it.

How should I approach it?


Solution

  • Have the server generate a pre-signed URL for the client to upload the image to. That means the server is in control of what the URLs will look like and it doesn't expose any secrets, yet the client can upload the image directly to S3.

    Generating a pre-signed URL in Python using boto3 looks something like this:

    s3 = boto3.client('s3', aws_access_key_id=..., aws_secret_access_key=...)
    params = dict(Bucket='my-bucket', Key='myfile.jpg', ContentType='image/jpeg')
    url = s3.generate_presigned_url('put_object', Params=params, ExpiresIn=600)
    

    The ContentType is optional, and the client will have to set the same Content-Type HTTP header during upload to url; I find it handy to limit the allowable file types if known.