bgneal@700: """This module contains a function to upload an image file to a S3Bucket.
bgneal@696: 
bgneal@700: The image can be resized and a thumbnail can be generated and uploaded as well.
bgneal@696: 
bgneal@696: """
bgneal@885: from base64 import b64encode
bgneal@696: import logging
bgneal@696: from io import BytesIO
bgneal@696: import os.path
bgneal@696: import uuid
bgneal@696: 
bgneal@696: from PIL import Image
bgneal@696: 
bgneal@971: from .utils import orient_image
bgneal@700: 
bgneal@696: 
bgneal@696: logger = logging.getLogger(__name__)
bgneal@696: 
bgneal@696: 
bgneal@885: def make_key():
bgneal@885:     """Generate a random key suitable for a filename"""
bgneal@885:     return b64encode(uuid.uuid4().bytes, '-_').rstrip('=')
bgneal@885: 
bgneal@885: 
bgneal@964: def upload(filename, bucket, metadata=None, new_size=None, thumb_size=None):
bgneal@700:     """Upload an image file to a given S3Bucket.
bgneal@696: 
bgneal@700:     The image can optionally be resized and a thumbnail can be generated and
bgneal@700:     uploaded as well.
bgneal@696: 
bgneal@700:     Parameters:
bgneal@964:         filename - The path to the file to process. The filename should have an
bgneal@964:                    extension, and this is used for the uploaded image & thumbnail
bgneal@964:                    names.
bgneal@700:         bucket - A core.s3.S3Bucket instance to upload to.
bgneal@700:         metadata - If not None, must be a dictionary of metadata to apply to the
bgneal@700:                    uploaded file and thumbnail.
bgneal@700:         new_size - If not None, the image will be resized to the dimensions
bgneal@700:                    specified by new_size, which must be a (width, height) tuple.
bgneal@700:         thumb_size - If not None, a thumbnail image will be created with the
bgneal@700:                      dimensions specified by thumb_size, which must be a (width,
bgneal@700:                      height) tuple. The thumbnail will use the same metadata, if
bgneal@700:                      present, as the image. The thumbnail filename will be the
bgneal@700:                      same basename as the image with a 't' appended. The
bgneal@700:                      extension will be the same as the original image.
bgneal@700: 
bgneal@700:     A tuple is returned: (image_url, thumb_url) where thumb_url will be None if
bgneal@700:     a thumbnail was not requested.
bgneal@696:     """
bgneal@696: 
bgneal@737:     logger.info('Processing image file: %s', filename)
bgneal@700: 
bgneal@885:     unique_key = make_key()
bgneal@964:     ext = os.path.splitext(filename)[1]
bgneal@696: 
bgneal@964:     # Re-orient if necessary
bgneal@964:     image = Image.open(filename)
bgneal@964:     changed, image = orient_image(image)
bgneal@964:     if changed:
bgneal@964:         image.save(filename)
bgneal@696: 
bgneal@964:     # Resize image if necessary
bgneal@964:     if new_size:
bgneal@964:         image = Image.open(filename)
bgneal@964:         if image.size > new_size:
bgneal@964:             logger.debug('Resizing from {} to {}'.format(image.size, new_size))
bgneal@964:             image.thumbnail(new_size, Image.ANTIALIAS)
bgneal@964:             image.save(filename)
bgneal@837: 
bgneal@964:     # Create thumbnail if necessary
bgneal@964:     thumb = None
bgneal@964:     if thumb_size:
bgneal@964:         logger.debug('Creating thumbnail {}'.format(thumb_size))
bgneal@964:         image = Image.open(filename)
bgneal@964:         image.thumbnail(thumb_size, Image.ANTIALIAS)
bgneal@964:         thumb = BytesIO()
bgneal@964:         image.save(thumb, format=image.format)
bgneal@696: 
bgneal@964:     # Upload images to S3
bgneal@964:     file_key = unique_key + ext
bgneal@964:     logging.debug('Uploading image')
bgneal@964:     image_url = bucket.upload_from_filename(file_key, filename, metadata)
bgneal@696: 
bgneal@700:     thumb_url = None
bgneal@700:     if thumb:
bgneal@700:         logging.debug('Uploading thumbnail')
bgneal@700:         thumb_key = '{}t{}'.format(unique_key, ext)
bgneal@700:         thumb_url = bucket.upload_from_string(thumb_key,
bgneal@700:                                               thumb.getvalue(),
bgneal@700:                                               metadata)
bgneal@696: 
bgneal@737:     logger.info('Completed processing image file: %s', filename)
bgneal@696: 
bgneal@700:     return (image_url, thumb_url)