Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask Upload Image to S3 without saving it to local file system

Tags:

flask

boto3

I need to upload a a user submitted photo to an s3 bucket. However I keep getting the following error:

TypeError: expected str, bytes or os.PathLike object, not FileStorage

How am I able to store the file as string/bytes instead of FileStorage? The relevent code is as follows:

@user_api.route('upload-profile-photo', methods=['PUT'])
@Auth.auth_required
def upload_profile_photo():
  """
  Upload User Profile Photo
  """
  key = Auth.auth_user()
  bucket = 'profile-photos'
  content_type = request.mimetype
  image_file = request.files['file']
  client = boto3.client('s3',
                        region_name='sfo2',
                        endpoint_url='https://example.xxx.amazonaws.com',
                        aws_access_key_id=os.environ['ACCESS_KEY'],
                        aws_secret_access_key=os.environ['SECRET_KEY'])
  with open(image_file, "rb") as f:
    client.upload_fileobj(
        bucket,
        f,
        key,
        ExtraArgs={'ACL': 'public-read', 'ContentType': content_type}
    )

  return custom_response({'message': 'image uploaded'}, 200)
like image 461
cp-stack Avatar asked Mar 22 '19 04:03

cp-stack


People also ask

How do I upload images to Amazon S3?

To upload folders and files to an S3 bucketSign in to the AWS Management Console and open the Amazon S3 console at https://console.aws.amazon.com/s3/ . In the Buckets list, choose the name of the bucket that you want to upload your folders or files to. Choose Upload.

What is S3 direct upload?

Updated: September 13, 2022. Amazon S3 Direct Upload for Web UI is an addon that allows Web UI to make use of S3 Direct Upload to directly upload files to a transient bucket, which will then be moved by the Nuxeo server to a final bucket once the upload is finished.


1 Answers

iTo achieve that with a FileStorage, I use the method put_object():

from werkzeug import secure_filename

@user_api.route('upload-profile-photo', methods=['PUT'])
@Auth.auth_required
def upload_profile_photo():
    """
    Upload User Profile Photo
    """
    key = Auth.auth_user()
    bucket = 'profile-photos'
    content_type = request.mimetype
    image_file = request.files['file']

    client = boto3.client('s3',
                          region_name='sfo2',
                          endpoint_url='https://example.xxx.amazonaws.com',
                          aws_access_key_id=os.environ['ACCESS_KEY'],
                          aws_secret_access_key=os.environ['SECRET_KEY'])

    filename = secure_filename(image_file.filename)  # This is convenient to validate your filename, otherwise just use file.filename

    client.put_object(Body=image_file,
                      Bucket=bucket,
                      Key=filename,
                      ContentType=content_type)

    return custom_response({'message': 'image uploaded'}, 200)

Note the call to secure_filename() is optional (you can simply pass image_file.filename), but can be very handy to validate the filename. Otherwise it would be nice to add some exception handlings, but the rough idea is here: no need to open() the file (that would need to be stored locally).

I encourage to have a look at the documentation here, to understand the difference with upload_fileobj()

like image 79
CloC Avatar answered Oct 21 '22 03:10

CloC