Mercurial > public > sg101
comparison core/image_uploader.py @ 964:51a2051588f5
Image uploading now expects a file.
Refactor image uploading to not expect a Django UploadedFile and use a regular
file instead. This will be needed for the future feature of being able to save
and upload images from the Internet.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Wed, 02 Sep 2015 20:50:08 -0500 |
parents | ee47122d6277 |
children |
comparison
equal
deleted
inserted
replaced
963:4619290d171d | 964:51a2051588f5 |
---|---|
5 """ | 5 """ |
6 from base64 import b64encode | 6 from base64 import b64encode |
7 import logging | 7 import logging |
8 from io import BytesIO | 8 from io import BytesIO |
9 import os.path | 9 import os.path |
10 import tempfile | |
11 import uuid | 10 import uuid |
12 | 11 |
13 from PIL import Image | 12 from PIL import Image |
14 | 13 |
15 from core.functions import temp_open | |
16 from core.image import orient_image | 14 from core.image import orient_image |
17 | 15 |
18 | 16 |
19 logger = logging.getLogger(__name__) | 17 logger = logging.getLogger(__name__) |
20 | 18 |
22 def make_key(): | 20 def make_key(): |
23 """Generate a random key suitable for a filename""" | 21 """Generate a random key suitable for a filename""" |
24 return b64encode(uuid.uuid4().bytes, '-_').rstrip('=') | 22 return b64encode(uuid.uuid4().bytes, '-_').rstrip('=') |
25 | 23 |
26 | 24 |
27 def upload(fp, bucket, metadata=None, new_size=None, thumb_size=None): | 25 def upload(filename, bucket, metadata=None, new_size=None, thumb_size=None): |
28 """Upload an image file to a given S3Bucket. | 26 """Upload an image file to a given S3Bucket. |
29 | 27 |
30 The image can optionally be resized and a thumbnail can be generated and | 28 The image can optionally be resized and a thumbnail can be generated and |
31 uploaded as well. | 29 uploaded as well. |
32 | 30 |
33 Parameters: | 31 Parameters: |
34 fp - The image file to process. This is expected to be an instance of | 32 filename - The path to the file to process. The filename should have an |
35 Django's UploadedFile class. The file must have a name attribute and | 33 extension, and this is used for the uploaded image & thumbnail |
36 we expect the name to have an extension. This is extension is | 34 names. |
37 used for the uploaded image and thumbnail names. | |
38 bucket - A core.s3.S3Bucket instance to upload to. | 35 bucket - A core.s3.S3Bucket instance to upload to. |
39 metadata - If not None, must be a dictionary of metadata to apply to the | 36 metadata - If not None, must be a dictionary of metadata to apply to the |
40 uploaded file and thumbnail. | 37 uploaded file and thumbnail. |
41 new_size - If not None, the image will be resized to the dimensions | 38 new_size - If not None, the image will be resized to the dimensions |
42 specified by new_size, which must be a (width, height) tuple. | 39 specified by new_size, which must be a (width, height) tuple. |
49 | 46 |
50 A tuple is returned: (image_url, thumb_url) where thumb_url will be None if | 47 A tuple is returned: (image_url, thumb_url) where thumb_url will be None if |
51 a thumbnail was not requested. | 48 a thumbnail was not requested. |
52 """ | 49 """ |
53 | 50 |
54 filename = fp.name | |
55 logger.info('Processing image file: %s', filename) | 51 logger.info('Processing image file: %s', filename) |
56 | 52 |
57 # Trying to use PIL (or Pillow) on a Django UploadedFile is often | |
58 # problematic because the file is often an in-memory file if it is under | |
59 # a certain size. This complicates matters and many of the operations we try | |
60 # to perform on it fail if this is the case. To get around these issues, | |
61 # we make a copy of the file on the file system and operate on the copy. | |
62 # First generate a unique name and temporary file path. | |
63 unique_key = make_key() | 53 unique_key = make_key() |
64 ext = os.path.splitext(fp.name)[1] | 54 ext = os.path.splitext(filename)[1] |
65 temp_name = os.path.join(tempfile.gettempdir(), unique_key + ext) | |
66 | 55 |
67 # Write the UploadedFile to a temporary file on disk | 56 # Re-orient if necessary |
68 with temp_open(temp_name, 'wb') as temp_file: | 57 image = Image.open(filename) |
69 for chunk in fp.chunks(): | 58 changed, image = orient_image(image) |
70 temp_file.write(chunk) | 59 if changed: |
71 temp_file.close() | 60 image.save(filename) |
72 | 61 |
73 # Re-orient if necessary | 62 # Resize image if necessary |
74 image = Image.open(temp_name) | 63 if new_size: |
75 changed, image = orient_image(image) | 64 image = Image.open(filename) |
76 if changed: | 65 if image.size > new_size: |
77 image.save(temp_name) | 66 logger.debug('Resizing from {} to {}'.format(image.size, new_size)) |
67 image.thumbnail(new_size, Image.ANTIALIAS) | |
68 image.save(filename) | |
78 | 69 |
79 # Resize image if necessary | 70 # Create thumbnail if necessary |
80 if new_size: | 71 thumb = None |
81 image = Image.open(temp_name) | 72 if thumb_size: |
82 if image.size > new_size: | 73 logger.debug('Creating thumbnail {}'.format(thumb_size)) |
83 logger.debug('Resizing from {} to {}'.format(image.size, new_size)) | 74 image = Image.open(filename) |
84 image.thumbnail(new_size, Image.ANTIALIAS) | 75 image.thumbnail(thumb_size, Image.ANTIALIAS) |
85 image.save(temp_name) | 76 thumb = BytesIO() |
77 image.save(thumb, format=image.format) | |
86 | 78 |
87 # Create thumbnail if necessary | 79 # Upload images to S3 |
88 thumb = None | 80 file_key = unique_key + ext |
89 if thumb_size: | 81 logging.debug('Uploading image') |
90 logger.debug('Creating thumbnail {}'.format(thumb_size)) | 82 image_url = bucket.upload_from_filename(file_key, filename, metadata) |
91 image = Image.open(temp_name) | |
92 image.thumbnail(thumb_size, Image.ANTIALIAS) | |
93 thumb = BytesIO() | |
94 image.save(thumb, format=image.format) | |
95 | |
96 # Upload images to S3 | |
97 file_key = unique_key + ext | |
98 logging.debug('Uploading image') | |
99 image_url = bucket.upload_from_filename(file_key, temp_name, metadata) | |
100 | 83 |
101 thumb_url = None | 84 thumb_url = None |
102 if thumb: | 85 if thumb: |
103 logging.debug('Uploading thumbnail') | 86 logging.debug('Uploading thumbnail') |
104 thumb_key = '{}t{}'.format(unique_key, ext) | 87 thumb_key = '{}t{}'.format(unique_key, ext) |