Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upload Base64 Image to S3 and return URL

I am trying to upload a base64 image to an S3 bucket using Python. I have googled and got a few answers but none of them works for me. And some answers use boto and not boto3, hence they are useless to me. I have also tried this link: Boto3: upload file from base64 to S3 but it is not working for me as Object method is unknown to the s3.

Following is my code so far:

import boto3

s3 = boto3.client('s3')
filename = photo.personId + '.png'
bucket_name = 'photos-collection'
dataToPutInS3 = base64.b64decode(photo.url[23:])

What is the correct way to upload this variable dataToPutInS3 data to s3 bucket and get a url back from it?

like image 948
Karan Sharma Avatar asked Oct 23 '19 05:10

Karan Sharma


2 Answers

You can convert your base64 to IO Bytes and use upload_fileobj to upload to S3 bucket.

import base64
import six
import uuid
import imghdr
import io


def get_file_extension(file_name, decoded_file):
    extension = imghdr.what(file_name, decoded_file)
    extension = "jpg" if extension == "jpeg" else extension
    return extension


def decode_base64_file(data):
    """
    Fuction to convert base 64 to readable IO bytes and auto-generate file name with extension
    :param data: base64 file input
    :return: tuple containing IO bytes file and filename
    """
    # Check if this is a base64 string
    if isinstance(data, six.string_types):
        # Check if the base64 string is in the "data:" format
        if 'data:' in data and ';base64,' in data:
            # Break out the header from the base64 content
            header, data = data.split(';base64,')

        # Try to decode the file. Return validation error if it fails.
        try:
            decoded_file = base64.b64decode(data)
        except TypeError:
            TypeError('invalid_image')

        # Generate file name:
        file_name = str(uuid.uuid4())[:12]  # 12 characters are more than enough.
        # Get the file name extension:
        file_extension = get_file_extension(file_name, decoded_file)

        complete_file_name = "%s.%s" % (file_name, file_extension,)

        return io.BytesIO(decoded_file), complete_file_name


def upload_base64_file(base64_file):
    bucket_name = 'your_bucket_name'
    file, file_name = decode_base64_file(base64_file)
    client = boto3.client('s3', aws_access_key_id='aws_access_key_id',
                          aws_secret_access_key='aws_secret_access_key')

    client.upload_fileobj(
        file,
        bucket_name,
        file_name,
        ExtraArgs={'ACL': 'public-read'}
    )
    return f"https://{bucket_name}.s3.amazonaws.com/{file_name}"
like image 187
p8ul Avatar answered Oct 28 '22 15:10

p8ul


You didn't mention how do you get the base64. In order to reproduce,my code snippet getting the image from the internet using the requests library and later convert it to base64 using the base64 library.

The trick here is to make sure the base64 string you want to upload doesn't include the data:image/jpeg;base64 prefix. And, as @dmigo mentioned in the comments, you should work with boto3.resource and not boto3.client.

    from botocore.vendored import requests
    import base64
    import boto3

    s3 = boto3.resource('s3')
    bucket_name = 'BukcetName'
    #where the file will be uploaded, if you want to upload the file to folder use 'Folder Name/FileName.jpeg'
    file_name_with_extention = 'FileName.jpeg'
    url_to_download = 'URL'

    #make sure there is no data:image/jpeg;base64 in the string that returns
    def get_as_base64(url):
        return base64.b64encode(requests.get(url).content)

    def lambda_handler(event, context):
        image_base64 = get_as_base64(url_to_download)
        obj = s3.Object(bucket_name,file_name_with_extention)
        obj.put(Body=base64.b64decode(image_base64))
        #get bucket location
        location = boto3.client('s3').get_bucket_location(Bucket=bucket_name)['LocationConstraint']
        #get object url
        object_url = "https://%s.s3-%s.amazonaws.com/%s" % (bucket_name,location, file_name_with_extention)
        print(object_url)

More about S3.Object.put.

like image 14
Amit Baranes Avatar answered Oct 28 '22 14:10

Amit Baranes