Mercurial > public > sg101
view core/image_uploader.py @ 837:234726f5a47a
For issue #74, re-orient uploaded images if necessary.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sat, 04 Oct 2014 13:43:22 -0500 |
parents | df2799f725d8 |
children | ee47122d6277 |
line wrap: on
line source
"""This module contains a function to upload an image file to a S3Bucket. The image can be resized and a thumbnail can be generated and uploaded as well. """ import logging from io import BytesIO import os.path import tempfile import uuid from PIL import Image from core.functions import temp_open from core.image import orient_image logger = logging.getLogger(__name__) def upload(fp, bucket, metadata=None, new_size=None, thumb_size=None): """Upload an image file to a given S3Bucket. The image can optionally be resized and a thumbnail can be generated and uploaded as well. Parameters: fp - The image file to process. This is expected to be an instance of Django's UploadedFile class. The file must have a name attribute and we expect the name to have an extension. This is extension is used for the uploaded image and thumbnail names. bucket - A core.s3.S3Bucket instance to upload to. metadata - If not None, must be a dictionary of metadata to apply to the uploaded file and thumbnail. new_size - If not None, the image will be resized to the dimensions specified by new_size, which must be a (width, height) tuple. thumb_size - If not None, a thumbnail image will be created with the dimensions specified by thumb_size, which must be a (width, height) tuple. The thumbnail will use the same metadata, if present, as the image. The thumbnail filename will be the same basename as the image with a 't' appended. The extension will be the same as the original image. A tuple is returned: (image_url, thumb_url) where thumb_url will be None if a thumbnail was not requested. """ filename = fp.name logger.info('Processing image file: %s', filename) # Trying to use PIL (or Pillow) on a Django UploadedFile is often # problematic because the file is often an in-memory file if it is under # a certain size. This complicates matters and many of the operations we try # to perform on it fail if this is the case. To get around these issues, # we make a copy of the file on the file system and operate on the copy. # First generate a unique name and temporary file path. unique_key = uuid.uuid4().hex ext = os.path.splitext(fp.name)[1] temp_name = os.path.join(tempfile.gettempdir(), unique_key + ext) # Write the UploadedFile to a temporary file on disk with temp_open(temp_name, 'wb') as temp_file: for chunk in fp.chunks(): temp_file.write(chunk) temp_file.close() # Re-orient if necessary image = Image.open(temp_name) changed, image = orient_image(image) if changed: image.save(temp_name) # Resize image if necessary if new_size: image = Image.open(temp_name) if image.size > new_size: logger.debug('Resizing from {} to {}'.format(image.size, new_size)) image.thumbnail(new_size, Image.ANTIALIAS) image.save(temp_name) # Create thumbnail if necessary thumb = None if thumb_size: logger.debug('Creating thumbnail {}'.format(thumb_size)) image = Image.open(temp_name) image.thumbnail(thumb_size, Image.ANTIALIAS) thumb = BytesIO() image.save(thumb, format=image.format) # Upload images to S3 file_key = unique_key + ext logging.debug('Uploading image') image_url = bucket.upload_from_filename(file_key, temp_name, metadata) thumb_url = None if thumb: logging.debug('Uploading thumbnail') thumb_key = '{}t{}'.format(unique_key, ext) thumb_url = bucket.upload_from_string(thumb_key, thumb.getvalue(), metadata) logger.info('Completed processing image file: %s', filename) return (image_url, thumb_url)