Mercurial > public > sg101
changeset 696:b2a8fde3173a
Got the image resizing and uploading working. It needs a lot of work though.
This commit is just to capture something that works.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sun, 08 Sep 2013 19:06:54 -0500 |
parents | 2d35e5f97a99 |
children | 67f8d49a9377 |
files | sg101/settings/base.py sg101/templates/user_photos/photo_detail.html user_photos/forms.py user_photos/images.py user_photos/models.py user_photos/urls.py user_photos/views.py |
diffstat | 7 files changed, 145 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/sg101/settings/base.py Sat Sep 07 20:50:46 2013 -0500 +++ b/sg101/settings/base.py Sun Sep 08 19:06:54 2013 -0500 @@ -292,9 +292,13 @@ WIKI_REDIS_SET = 'wiki_cookie_keys' # User photo upload settings -USER_PHOTO_ENABLED = True -USER_PHOTO_ACCESS_KEY = SECRETS['AWS_ACCESS_KEY'] -USER_PHOTO_SECRET_KEY = SECRETS['AWS_SECRET_KEY'] +USER_PHOTOS_ENABLED = True +USER_PHOTOS_ACCESS_KEY = SECRETS['AWS_ACCESS_KEY'] +USER_PHOTOS_SECRET_KEY = SECRETS['AWS_SECRET_KEY'] +USER_PHOTOS_BUCKET = 'sg101.user.photos' +USER_PHOTOS_BASE_URL = 'https://s3-us-west-1.amazonaws.com' +USER_PHOTOS_MAX_SIZE = (660, 720) +USER_PHOTOS_THUMB_SIZE = (120, 120) ####################################################################### # Asynchronous settings (queues, queued_search, redis, celery, etc)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sg101/templates/user_photos/photo_detail.html Sun Sep 08 19:06:54 2013 -0500 @@ -0,0 +1,18 @@ +{% extends 'base.html' %} +{% block title %}User Photo Details{% endblock %} +{% block content %} +<h2>User Photo Details</h2> + +<ul> + <li>Uploader: {{ object.user.username }}</li> + <li>Upload date: {{ object.upload_date|date }}</li> +</li> + +<div> + <img src="{{ object.thumb_url }}" alt="thumbnail" /> +</div> + +<div> + <img src="{{ object.url }}" alt="photo" /> +</div> +{% endblock %}
--- a/user_photos/forms.py Sat Sep 07 20:50:46 2013 -0500 +++ b/user_photos/forms.py Sun Sep 08 19:06:54 2013 -0500 @@ -1,6 +1,25 @@ """Forms for the user_photos application.""" from django import forms +from user_photos.models import Photo +from user_photos.images import process_file + class UploadForm(forms.Form): image_file = forms.ImageField() + + def __init__(self, *args, **kwargs): + self.user = kwargs.pop('user') + super(UploadForm, self).__init__(*args, **kwargs) + + def save(self): + """Processes the image and creates a new Photo object, which is saved to + the database. The new Photo instance is returned. + + This function should only be called if is_valid() returns True. + + """ + url, thumb_url = process_file(self.cleaned_data['image_file'], self.user) + photo = Photo(user=self.user, url=url, thumb_url=thumb_url) + photo.save() + return photo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user_photos/images.py Sun Sep 08 19:06:54 2013 -0500 @@ -0,0 +1,86 @@ +"""images.py + +This module contains image processing routines for the user_photos application. + +""" +import datetime +import logging +from io import BytesIO +import os.path +import uuid + +from django.conf import settings +from PIL import Image +from boto.s3.connection import S3Connection +from boto.s3.key import Key + + +logger = logging.getLogger(__name__) + + +def process_file(f, user): + """Perform processing on the given uploaded image file: + + * The image is resized if necessary + * A thumbnail version is created + * The image and thumbnail are uploaded to an S3 bucket + * The image and thumbnail URLs are returned as a tuple + + """ + logger.info('Processing image file for {}: {}'.format(user.username, f.name)) + + unique_key = uuid.uuid4().hex + ext = os.path.splitext(f.name)[1] + filename = '/tmp/' + unique_key + ext + with open(filename, 'wb') as fp: + for chunk in f.chunks(): + fp.write(chunk) + + # Resize image if necessary + image = Image.open(filename) + if image.size > settings.USER_PHOTOS_MAX_SIZE: + logger.debug('Resizing from {} to {}'.format(image.size, settings.USER_PHOTOS_MAX_SIZE)) + image.thumbnail(settings.USER_PHOTOS_MAX_SIZE, Image.ANTIALIAS) + image.save(filename) + + # Create thumbnail + logger.debug('Creating thumbnail') + image = Image.open(filename) + image.thumbnail(settings.USER_PHOTOS_THUMB_SIZE, Image.ANTIALIAS) + thumb = BytesIO() + image.save(thumb, format=image.format) + + # Upload both images to S3 + logger.debug('Getting connection / bucket') + conn = S3Connection(settings.USER_PHOTOS_ACCESS_KEY, + settings.USER_PHOTOS_SECRET_KEY) + bucket = conn.get_bucket(settings.USER_PHOTOS_BUCKET, validate=False) + + now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + logging.debug('Uploading image') + k1 = Key(bucket) + k1.key = unique_key + ext + k1.set_metadata('user', user.username) + k1.set_metadata('date', now) + k1.set_contents_from_filename(filename) + + logging.debug('Uploading thumbnail') + k2 = Key(bucket) + k2.key = '{}t{}'.format(unique_key, ext) + k2.set_metadata('user', user.username) + k2.set_metadata('date', now) + k2.set_contents_from_string(thumb.getvalue()) + + logging.debug('Making public') + k1.make_public() + k2.make_public() + + os.remove(filename) + + logger.info('Completed processing image file for {}: {}'.format(user.username, f.name)) + + url_base = '{}/{}/'.format(settings.USER_PHOTOS_BASE_URL, + settings.USER_PHOTOS_BUCKET) + + return (url_base + k1.key, url_base + k2.key)
--- a/user_photos/models.py Sat Sep 07 20:50:46 2013 -0500 +++ b/user_photos/models.py Sun Sep 08 19:06:54 2013 -0500 @@ -4,6 +4,7 @@ from django.db import models from django.conf import settings +from django.core.urlresolvers import reverse class Photo(models.Model): @@ -19,7 +20,7 @@ self.upload_date.strftime('%Y-%m-%d %H:%M:%S')) def get_absolute_url(self): - return self.url + return reverse('user_photos-detail', kwargs={'pk': self.pk}) def save(self, *args, **kwargs): if not self.pk and not self.upload_date:
--- a/user_photos/urls.py Sat Sep 07 20:50:46 2013 -0500 +++ b/user_photos/urls.py Sun Sep 08 19:06:54 2013 -0500 @@ -1,6 +1,13 @@ """URLs for the user_photos application.""" from django.conf.urls import patterns, url +from django.views.generic import DetailView + +from user_photos.models import Photo + urlpatterns = patterns('user_photos.views', url(r'^upload/$', 'upload', name='user_photos-upload'), + url(r'^photo/(?P<pk>\d+)/$', + DetailView.as_view(model=Photo), + name='user_photos-detail') )
--- a/user_photos/views.py Sat Sep 07 20:50:46 2013 -0500 +++ b/user_photos/views.py Sun Sep 08 19:06:54 2013 -0500 @@ -1,7 +1,7 @@ """Views for the user_photos application.""" from django.conf import settings from django.contrib.auth.decorators import login_required -from django.shortcuts import render +from django.shortcuts import render, redirect from user_photos.forms import UploadForm @@ -19,17 +19,16 @@ """ form = None - uploads_enabled = settings.USER_PHOTO_ENABLED + uploads_enabled = settings.USER_PHOTOS_ENABLED if uploads_enabled: if request.method == 'POST': - form = UploadForm(request.POST, request.FILES) + form = UploadForm(request.POST, request.FILES, user=request.user) if form.is_valid(): - #TODO - print "**************", request.FILES['image_file'] - pass + photo = form.save() + return redirect(photo) else: - form = UploadForm() + form = UploadForm(user=request.user) return render(request, 'user_photos/upload_form.html', { 'enabled': uploads_enabled,