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@885
|
6 from base64 import b64encode
|
bgneal@1195
|
7 import datetime
|
bgneal@696
|
8 import logging
|
bgneal@696
|
9 from io import BytesIO
|
bgneal@696
|
10 import os.path
|
bgneal@1195
|
11 import shutil
|
bgneal@696
|
12 import uuid
|
bgneal@696
|
13
|
bgneal@1195
|
14 from django.conf import settings
|
bgneal@1195
|
15 from django.contrib.sites.models import Site
|
bgneal@696
|
16 from PIL import Image
|
bgneal@696
|
17
|
bgneal@971
|
18 from .utils import orient_image
|
bgneal@1195
|
19 from core.s3 import S3Bucket
|
bgneal@700
|
20
|
bgneal@696
|
21
|
bgneal@696
|
22 logger = logging.getLogger(__name__)
|
bgneal@696
|
23
|
bgneal@696
|
24
|
bgneal@1195
|
25 def process_upload(user, filename):
|
bgneal@1195
|
26 if settings.USER_PHOTOS_LOCAL_UPLOAD:
|
bgneal@1195
|
27 url, thumb_url = _process_local_upload(
|
bgneal@1195
|
28 filename,
|
bgneal@1195
|
29 new_size=settings.USER_PHOTOS_MAX_SIZE,
|
bgneal@1195
|
30 thumb_size=settings.USER_PHOTOS_THUMB_SIZE)
|
bgneal@1195
|
31 else:
|
bgneal@1195
|
32 now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
bgneal@1195
|
33 metadata = {'user': user.username, 'date': now}
|
bgneal@1195
|
34
|
bgneal@1195
|
35 bucket = S3Bucket(access_key=settings.USER_PHOTOS_ACCESS_KEY,
|
bgneal@1195
|
36 secret_key=settings.USER_PHOTOS_SECRET_KEY,
|
bgneal@1195
|
37 base_url=settings.USER_PHOTOS_BASE_URL,
|
bgneal@1195
|
38 bucket_name=settings.USER_PHOTOS_BUCKET)
|
bgneal@1195
|
39
|
bgneal@1195
|
40 url, thumb_url = upload(filename,
|
bgneal@1195
|
41 bucket=bucket,
|
bgneal@1195
|
42 metadata=metadata,
|
bgneal@1195
|
43 new_size=settings.USER_PHOTOS_MAX_SIZE,
|
bgneal@1195
|
44 thumb_size=settings.USER_PHOTOS_THUMB_SIZE)
|
bgneal@1195
|
45 return (url, thumb_url)
|
bgneal@1195
|
46
|
bgneal@1195
|
47
|
bgneal@885
|
48 def make_key():
|
bgneal@885
|
49 """Generate a random key suitable for a filename"""
|
bgneal@885
|
50 return b64encode(uuid.uuid4().bytes, '-_').rstrip('=')
|
bgneal@885
|
51
|
bgneal@885
|
52
|
bgneal@964
|
53 def upload(filename, bucket, metadata=None, new_size=None, thumb_size=None):
|
bgneal@700
|
54 """Upload an image file to a given S3Bucket.
|
bgneal@696
|
55
|
bgneal@700
|
56 The image can optionally be resized and a thumbnail can be generated and
|
bgneal@700
|
57 uploaded as well.
|
bgneal@696
|
58
|
bgneal@700
|
59 Parameters:
|
bgneal@964
|
60 filename - The path to the file to process. The filename should have an
|
bgneal@964
|
61 extension, and this is used for the uploaded image & thumbnail
|
bgneal@964
|
62 names.
|
bgneal@700
|
63 bucket - A core.s3.S3Bucket instance to upload to.
|
bgneal@700
|
64 metadata - If not None, must be a dictionary of metadata to apply to the
|
bgneal@700
|
65 uploaded file and thumbnail.
|
bgneal@700
|
66 new_size - If not None, the image will be resized to the dimensions
|
bgneal@700
|
67 specified by new_size, which must be a (width, height) tuple.
|
bgneal@700
|
68 thumb_size - If not None, a thumbnail image will be created with the
|
bgneal@700
|
69 dimensions specified by thumb_size, which must be a (width,
|
bgneal@700
|
70 height) tuple. The thumbnail will use the same metadata, if
|
bgneal@700
|
71 present, as the image. The thumbnail filename will be the
|
bgneal@700
|
72 same basename as the image with a 't' appended. The
|
bgneal@700
|
73 extension will be the same as the original image.
|
bgneal@700
|
74
|
bgneal@700
|
75 A tuple is returned: (image_url, thumb_url) where thumb_url will be None if
|
bgneal@700
|
76 a thumbnail was not requested.
|
bgneal@696
|
77 """
|
bgneal@696
|
78
|
bgneal@737
|
79 logger.info('Processing image file: %s', filename)
|
bgneal@700
|
80
|
bgneal@885
|
81 unique_key = make_key()
|
bgneal@964
|
82 ext = os.path.splitext(filename)[1]
|
bgneal@696
|
83
|
bgneal@964
|
84 # Re-orient if necessary
|
bgneal@964
|
85 image = Image.open(filename)
|
bgneal@964
|
86 changed, image = orient_image(image)
|
bgneal@964
|
87 if changed:
|
bgneal@964
|
88 image.save(filename)
|
bgneal@696
|
89
|
bgneal@964
|
90 # Resize image if necessary
|
bgneal@964
|
91 if new_size:
|
bgneal@964
|
92 image = Image.open(filename)
|
bgneal@964
|
93 if image.size > new_size:
|
bgneal@964
|
94 logger.debug('Resizing from {} to {}'.format(image.size, new_size))
|
bgneal@964
|
95 image.thumbnail(new_size, Image.ANTIALIAS)
|
bgneal@964
|
96 image.save(filename)
|
bgneal@837
|
97
|
bgneal@964
|
98 # Create thumbnail if necessary
|
bgneal@964
|
99 thumb = None
|
bgneal@964
|
100 if thumb_size:
|
bgneal@964
|
101 logger.debug('Creating thumbnail {}'.format(thumb_size))
|
bgneal@964
|
102 image = Image.open(filename)
|
bgneal@964
|
103 image.thumbnail(thumb_size, Image.ANTIALIAS)
|
bgneal@964
|
104 thumb = BytesIO()
|
bgneal@964
|
105 image.save(thumb, format=image.format)
|
bgneal@696
|
106
|
bgneal@964
|
107 # Upload images to S3
|
bgneal@964
|
108 file_key = unique_key + ext
|
bgneal@964
|
109 logging.debug('Uploading image')
|
bgneal@964
|
110 image_url = bucket.upload_from_filename(file_key, filename, metadata)
|
bgneal@696
|
111
|
bgneal@700
|
112 thumb_url = None
|
bgneal@700
|
113 if thumb:
|
bgneal@700
|
114 logging.debug('Uploading thumbnail')
|
bgneal@700
|
115 thumb_key = '{}t{}'.format(unique_key, ext)
|
bgneal@700
|
116 thumb_url = bucket.upload_from_string(thumb_key,
|
bgneal@700
|
117 thumb.getvalue(),
|
bgneal@700
|
118 metadata)
|
bgneal@696
|
119
|
bgneal@737
|
120 logger.info('Completed processing image file: %s', filename)
|
bgneal@696
|
121
|
bgneal@700
|
122 return (image_url, thumb_url)
|
bgneal@1195
|
123
|
bgneal@1195
|
124
|
bgneal@1195
|
125 def _process_local_upload(filename, new_size=None, thumb_size=None):
|
bgneal@1195
|
126 # Re-orient if necessary
|
bgneal@1195
|
127 image = Image.open(filename)
|
bgneal@1195
|
128 changed, image = orient_image(image)
|
bgneal@1195
|
129 if changed:
|
bgneal@1195
|
130 image.save(filename)
|
bgneal@1195
|
131
|
bgneal@1195
|
132 # Resize image if necessary
|
bgneal@1195
|
133 if new_size:
|
bgneal@1195
|
134 image = Image.open(filename)
|
bgneal@1195
|
135 if image.size > new_size:
|
bgneal@1195
|
136 logger.debug('Resizing from {} to {}'.format(image.size, new_size))
|
bgneal@1195
|
137 image.thumbnail(new_size, Image.ANTIALIAS)
|
bgneal@1195
|
138 image.save(filename)
|
bgneal@1195
|
139
|
bgneal@1195
|
140 # Create thumbnail if necessary
|
bgneal@1195
|
141 thumb = None
|
bgneal@1195
|
142 if thumb_size:
|
bgneal@1195
|
143 logger.debug('Creating thumbnail {}'.format(thumb_size))
|
bgneal@1195
|
144 image = Image.open(filename)
|
bgneal@1195
|
145 image.thumbnail(thumb_size, Image.ANTIALIAS)
|
bgneal@1195
|
146 thumb = BytesIO()
|
bgneal@1195
|
147 image.save(thumb, format=image.format)
|
bgneal@1195
|
148
|
bgneal@1195
|
149 # Copy file and thumbnail to our user upload files directory.
|
bgneal@1195
|
150 upload_dir = os.path.join(settings.MEDIA_ROOT,
|
bgneal@1195
|
151 settings.USER_PHOTOS_LOCAL_UPLOAD_DIR)
|
bgneal@1195
|
152 unique_name = uuid.uuid4().hex
|
bgneal@1195
|
153 ext = os.path.splitext(filename)[1]
|
bgneal@1195
|
154 image_name = '%s%s' % (unique_name, ext)
|
bgneal@1195
|
155 thumb_name = '%st%s' % (unique_name, ext)
|
bgneal@1195
|
156 image_path = os.path.join(upload_dir, image_name)
|
bgneal@1195
|
157 thumb_path = os.path.join(upload_dir, thumb_name)
|
bgneal@1195
|
158
|
bgneal@1195
|
159 shutil.copy(filename, image_path)
|
bgneal@1195
|
160 if thumb:
|
bgneal@1195
|
161 shutil.copyfileobj(thumb, thumb_path)
|
bgneal@1195
|
162
|
bgneal@1195
|
163 # Generate URLs for the image and thumb.
|
bgneal@1195
|
164 site = Site.objects.get_current()
|
bgneal@1195
|
165
|
bgneal@1195
|
166 url_pattern = '%s://%s%s%s/%s'
|
bgneal@1195
|
167
|
bgneal@1195
|
168 image_url = url_pattern % (
|
bgneal@1195
|
169 settings.SITE_SCHEME,
|
bgneal@1195
|
170 site.domain,
|
bgneal@1195
|
171 settings.MEDIA_URL,
|
bgneal@1195
|
172 settings.USER_PHOTOS_LOCAL_UPLOAD_DIR,
|
bgneal@1195
|
173 image_name)
|
bgneal@1195
|
174
|
bgneal@1195
|
175 thumb_url = url_pattern % (
|
bgneal@1195
|
176 settings.SITE_SCHEME,
|
bgneal@1195
|
177 site.domain,
|
bgneal@1195
|
178 settings.MEDIA_URL,
|
bgneal@1195
|
179 settings.USER_PHOTOS_LOCAL_UPLOAD_DIR,
|
bgneal@1195
|
180 thumb_name)
|
bgneal@1195
|
181
|
bgneal@1195
|
182 return (image_url, thumb_url)
|
bgneal@1195
|
183
|