Mercurial > public > sg101
comparison core/image_uploader.py @ 700:e888d627928f
Refactored the processing of image uploads.
I suspect I will use this general algorithm in other places (like POTD), so
I made it reusable.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Wed, 11 Sep 2013 20:31:23 -0500 |
parents | user_photos/images.py@67f8d49a9377 |
children | 775e7f74096a |
comparison
equal
deleted
inserted
replaced
699:d33bedc3be74 | 700:e888d627928f |
---|---|
1 """This module contains a function to upload an image file to a S3Bucket. | |
2 | |
3 The image can be resized and a thumbnail can be generated and uploaded as well. | |
4 | |
5 """ | |
6 import logging | |
7 from io import BytesIO | |
8 import os.path | |
9 import tempfile | |
10 import uuid | |
11 | |
12 from PIL import Image | |
13 | |
14 from core.functions import temp_open | |
15 | |
16 | |
17 logger = logging.getLogger(__name__) | |
18 | |
19 | |
20 def upload(fp, bucket, metadata=None, new_size=None, thumb_size=None): | |
21 """Upload an image file to a given S3Bucket. | |
22 | |
23 The image can optionally be resized and a thumbnail can be generated and | |
24 uploaded as well. | |
25 | |
26 Parameters: | |
27 fp - The image file to process. This is expected to be an instance of | |
28 Django's UploadedFile class. The file must have a name attribute and | |
29 we expect the name to have an extension. This is extension is | |
30 used for the uploaded image and thumbnail names. | |
31 bucket - A core.s3.S3Bucket instance to upload to. | |
32 metadata - If not None, must be a dictionary of metadata to apply to the | |
33 uploaded file and thumbnail. | |
34 new_size - If not None, the image will be resized to the dimensions | |
35 specified by new_size, which must be a (width, height) tuple. | |
36 thumb_size - If not None, a thumbnail image will be created with the | |
37 dimensions specified by thumb_size, which must be a (width, | |
38 height) tuple. The thumbnail will use the same metadata, if | |
39 present, as the image. The thumbnail filename will be the | |
40 same basename as the image with a 't' appended. The | |
41 extension will be the same as the original image. | |
42 | |
43 A tuple is returned: (image_url, thumb_url) where thumb_url will be None if | |
44 a thumbnail was not requested. | |
45 """ | |
46 | |
47 logger.info('Processing image file: {}'.format(fp.name)) | |
48 | |
49 # Trying to use PIL (or Pillow) on a Django UploadedFile is often | |
50 # problematic because the file is often an in-memory file if it is under | |
51 # a certain size. This complicates matters and many of the operations we try | |
52 # to perform on it fail if this is the case. To get around these issues, | |
53 # we make a copy of the file on the file system and operate on the copy. | |
54 # First generate a unique name and temporary file path. | |
55 unique_key = uuid.uuid4().hex | |
56 ext = os.path.splitext(fp.name)[1] | |
57 temp_name = os.path.join(tempfile.gettempdir(), unique_key + ext) | |
58 | |
59 # Write the UploadedFile to a temporary file on disk | |
60 with temp_open(temp_name, 'wb') as temp_file: | |
61 for chunk in fp.chunks(): | |
62 temp_file.write(chunk) | |
63 temp_file.close() | |
64 | |
65 # Resize image if necessary | |
66 if new_size: | |
67 image = Image.open(temp_name) | |
68 if image.size > new_size: | |
69 logger.debug('Resizing from {} to {}'.format(image.size, new_size)) | |
70 image.thumbnail(new_size, Image.ANTIALIAS) | |
71 image.save(temp_name) | |
72 | |
73 # Create thumbnail if necessary | |
74 thumb = None | |
75 if thumb_size: | |
76 logger.debug('Creating thumbnail {}'.format(thumb_size)) | |
77 image = Image.open(temp_name) | |
78 image.thumbnail(thumb_size, Image.ANTIALIAS) | |
79 thumb = BytesIO() | |
80 image.save(thumb, format=image.format) | |
81 | |
82 # Upload images to S3 | |
83 file_key = unique_key + ext | |
84 logging.debug('Uploading image') | |
85 image_url = bucket.upload_from_filename(file_key, temp_name, metadata) | |
86 | |
87 thumb_url = None | |
88 if thumb: | |
89 logging.debug('Uploading thumbnail') | |
90 thumb_key = '{}t{}'.format(unique_key, ext) | |
91 thumb_url = bucket.upload_from_string(thumb_key, | |
92 thumb.getvalue(), | |
93 metadata) | |
94 | |
95 logger.info('Completed processing image file: {}'.format(fp.name)) | |
96 | |
97 return (image_url, thumb_url) |