changeset 1195:7fc6c42b2f5b

Adding a local user photo upload option.
author Brian Neal <bgneal@gmail.com>
date Sun, 07 May 2023 16:22:13 -0500
parents d437d5b88c27
children e3a7e876aca7
files core/images/upload.py sg101/settings/base.py sg101/settings/production.py user_photos/forms.py
diffstat 4 files changed, 97 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/core/images/upload.py	Sun Mar 19 10:36:38 2023 -0500
+++ b/core/images/upload.py	Sun May 07 16:22:13 2023 -0500
@@ -4,19 +4,47 @@
 
 """
 from base64 import b64encode
+import datetime
 import logging
 from io import BytesIO
 import os.path
+import shutil
 import uuid
 
+from django.conf import settings
+from django.contrib.sites.models import Site
 from PIL import Image
 
 from .utils import orient_image
+from core.s3 import S3Bucket
 
 
 logger = logging.getLogger(__name__)
 
 
+def process_upload(user, filename):
+    if settings.USER_PHOTOS_LOCAL_UPLOAD:
+        url, thumb_url = _process_local_upload(
+                filename,
+                new_size=settings.USER_PHOTOS_MAX_SIZE,
+                thumb_size=settings.USER_PHOTOS_THUMB_SIZE)
+    else:
+        now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+        metadata = {'user': user.username, 'date': now}
+
+        bucket = S3Bucket(access_key=settings.USER_PHOTOS_ACCESS_KEY,
+                          secret_key=settings.USER_PHOTOS_SECRET_KEY,
+                          base_url=settings.USER_PHOTOS_BASE_URL,
+                          bucket_name=settings.USER_PHOTOS_BUCKET)
+
+        url, thumb_url = upload(filename,
+                                bucket=bucket,
+                                metadata=metadata,
+                                new_size=settings.USER_PHOTOS_MAX_SIZE,
+                                thumb_size=settings.USER_PHOTOS_THUMB_SIZE)
+    return (url, thumb_url)
+
+
 def make_key():
     """Generate a random key suitable for a filename"""
     return b64encode(uuid.uuid4().bytes, '-_').rstrip('=')
@@ -92,3 +120,64 @@
     logger.info('Completed processing image file: %s', filename)
 
     return (image_url, thumb_url)
+
+
+def _process_local_upload(filename, new_size=None, thumb_size=None):
+    # Re-orient if necessary
+    image = Image.open(filename)
+    changed, image = orient_image(image)
+    if changed:
+        image.save(filename)
+
+    # Resize image if necessary
+    if new_size:
+        image = Image.open(filename)
+        if image.size > new_size:
+            logger.debug('Resizing from {} to {}'.format(image.size, new_size))
+            image.thumbnail(new_size, Image.ANTIALIAS)
+            image.save(filename)
+
+    # Create thumbnail if necessary
+    thumb = None
+    if thumb_size:
+        logger.debug('Creating thumbnail {}'.format(thumb_size))
+        image = Image.open(filename)
+        image.thumbnail(thumb_size, Image.ANTIALIAS)
+        thumb = BytesIO()
+        image.save(thumb, format=image.format)
+
+    # Copy file and thumbnail to our user upload files directory.
+    upload_dir = os.path.join(settings.MEDIA_ROOT,
+                              settings.USER_PHOTOS_LOCAL_UPLOAD_DIR)
+    unique_name = uuid.uuid4().hex
+    ext = os.path.splitext(filename)[1]
+    image_name = '%s%s' % (unique_name, ext)
+    thumb_name = '%st%s' % (unique_name, ext)
+    image_path = os.path.join(upload_dir, image_name)
+    thumb_path = os.path.join(upload_dir, thumb_name)
+
+    shutil.copy(filename, image_path)
+    if thumb:
+        shutil.copyfileobj(thumb, thumb_path)
+
+    # Generate URLs for the image and thumb.
+    site = Site.objects.get_current()
+
+    url_pattern = '%s://%s%s%s/%s'
+
+    image_url = url_pattern % (
+            settings.SITE_SCHEME,
+            site.domain,
+            settings.MEDIA_URL,
+            settings.USER_PHOTOS_LOCAL_UPLOAD_DIR,
+            image_name)
+
+    thumb_url = url_pattern % (
+            settings.SITE_SCHEME,
+            site.domain,
+            settings.MEDIA_URL,
+            settings.USER_PHOTOS_LOCAL_UPLOAD_DIR,
+            thumb_name)
+
+    return (image_url, thumb_url)
+
--- a/sg101/settings/base.py	Sun Mar 19 10:36:38 2023 -0500
+++ b/sg101/settings/base.py	Sun May 07 16:22:13 2023 -0500
@@ -307,6 +307,9 @@
     'lh3.googleusercontent.com',
 ]
 
+USER_PHOTOS_LOCAL_UPLOAD = False
+USER_PHOTOS_LOCAL_UPLOAD_DIR = 'user_photos'
+
 # If this flag is False, the queued_search queue will not be processed. This is
 # useful when we are rebuilding the search index.
 SEARCH_QUEUE_ENABLED = True
--- a/sg101/settings/production.py	Sun Mar 19 10:36:38 2023 -0500
+++ b/sg101/settings/production.py	Sun May 07 16:22:13 2023 -0500
@@ -54,6 +54,8 @@
 
 DONATIONS_DEBUG = False
 
+USER_PHOTOS_LOCAL_UPLOAD = False
+
 # SSL related settings
 SESSION_COOKIE_SECURE = True
 CSRF_COOKIE_SECURE = True
--- a/user_photos/forms.py	Sun Mar 19 10:36:38 2023 -0500
+++ b/user_photos/forms.py	Sun May 07 16:22:13 2023 -0500
@@ -1,5 +1,4 @@
 """Forms for the user_photos application."""
-import datetime
 import hashlib
 import os.path
 import urlparse
@@ -9,8 +8,7 @@
 
 from core.download import download_file
 from core.functions import remove_file, TemporaryFile
-from core.images.upload import upload
-from core.s3 import S3Bucket
+from core.images.upload import process_upload
 from core.services import get_redis_connection
 from user_photos.models import Photo
 
@@ -70,12 +68,6 @@
         except Photo.DoesNotExist:
             pass
 
-        # This must not be a duplicate, proceed with upload to S3
-        bucket = S3Bucket(access_key=settings.USER_PHOTOS_ACCESS_KEY,
-                          secret_key=settings.USER_PHOTOS_SECRET_KEY,
-                          base_url=settings.USER_PHOTOS_BASE_URL,
-                          bucket_name=settings.USER_PHOTOS_BUCKET)
-
         # 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
@@ -91,14 +83,7 @@
                 t.file.write(chunk)
             t.file.close()
 
-            now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
-            metadata = {'user': self.user.username, 'date': now}
-
-            url, thumb_url = upload(filename=t.filename,
-                                    bucket=bucket,
-                                    metadata=metadata,
-                                    new_size=settings.USER_PHOTOS_MAX_SIZE,
-                                    thumb_size=settings.USER_PHOTOS_THUMB_SIZE)
+            url, thumb_url = process_upload(self.user, t.filename)
 
         photo = Photo(user=self.user, url=url, thumb_url=thumb_url,
                 signature=signature)
@@ -151,19 +136,6 @@
 
         # Try to download the file
         with remove_file(download_file(url)) as path:
-
-            # Upload it to our S3 bucket
-            bucket = S3Bucket(access_key=settings.USER_PHOTOS_ACCESS_KEY,
-                              secret_key=settings.USER_PHOTOS_SECRET_KEY,
-                              base_url=settings.HOT_LINK_PHOTOS_BASE_URL,
-                              bucket_name=settings.HOT_LINK_PHOTOS_BUCKET)
-
-            now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
-            metadata = {'user': self.user.username, 'date': now}
-
-            url, _ = upload(filename=path,
-                            bucket=bucket,
-                            metadata=metadata,
-                            new_size=settings.USER_PHOTOS_MAX_SIZE)
+            url, _ = process_upload(self.user, path)
 
         return url