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