Challenges:
- the PIL (python image library) that most stack overflow questions referenced is no longer supported. Pillow, a fork of that library, is still supported, however.
- The Pillow tutorial for making a thumbnail referred to opening files from the users file structure, but I wanted to use the Image file object without writing it to disk.
I wrote a step by step guide since others in the class had expressed interest in this. I'm fairly proud that I was able to get all this to work. I'd like to, next, get it to run in the background since the user experience is currently held up while the files upload to S3 and get resized.
Guide:
Creating thumbnails from uploaded files (in django/python
2.7)
ultimate goal: accept a file from a user, create a thumbnail
from the file, upload both original file and thumbnail to S3, and record link
to respective files. Note: this does not
take into account a user uploading an enormous file which would have to be
processed in pieces (Django default limit is 2.5 MB)
preparatory stuff:
a. pip install Pillow (http://pillow.readthedocs.org/en/3.1.x/installation.html),
a python module for image manipulation.
Any stack overflow answers that refer to PIL (python image library)
should work as written, so long as you have installed Pillow. (PIL is no longer
maintained, but Pillow is a fork that is still being maintained). Exceptions may exist
b. in views:
from
django_boto.s3 import upload
import
StringIO
from PIL
import Image
from
django.core.files.uploadedfile import InMemoryUploadedFile
1. I needed
to create a form that included a file upload as one of several fields (vs. a
form that accepted ONLY a file upload)
corresponding html for the upload
file input (name=”pic”). The
accept=”image/*” means that the form will post only when an image file has been
selected. The * means any image format
is ok. You could constrain the format to
jpg, tiff, png etc.
2. Once the form is
sent via POST request, the file is accessed via request.FILES[‘pic’]
new_upload=request.FILES[‘pic’]
3. Open the uploaded file as an Image object (from the
Pillow library):
newThumb=Image.open(StringIO.StringIO(new_upload.read())
4. set a variable that contains a size for the thumbnail. The format is a tuple with height and width
(h,w). To keep the same proportions, but
resize for the long side to be 128 px, for example:
5. make the damn thumbnail! Note that calling the .thumbnail
method changes the Image object in place.
You cannot set newThumb.thumbnail equal to a variable. The Image.ANTIALIAS ensures your downsized
image still looks reasonably good)
newThumb.thumbnail(thumb_size,
Image.ANTIALIAS)
6. You need a
name for the new thumbnail file.
Remember, “new_upload) is an object.
It has already been stripped of it’s file path, but it still has the
original upload name (eg. “fancycat.jpg”)
upload_name
= new_upload.name.split(.)
thumb_name=upload_name[0]
+ “_thumb” + upload_name[1]
result is “fancycat_thumb.jpg”
7. The fun
part! Create a “django file-like object”
to hold a temporary version of your thumbnail.
Using this method, you don’t have actually create a file and write it to
your file system. (source: http://stackoverflow.com/questions/3723220/how-do-you-convert-a-pil-image-to-a-django-file)
Think of how you do
with
(“file.txt”, wb+) as f:
f.readlines()
‘f’ is the ‘file-like object’
To create the file-like object,
however, you use:
thumb_io=StringIO.StringIO()
8. save the
thumbnail image object (newThumb) to the file-like object using the Image
method .save():
newThumb.save(thumb_io,
format=”JPEG”)
9. Associate
file properties with the file-like object.
(I think that’s what this step is doing.
I’ll give you a dollar/550 colones if you want to look up the documentation
because it’s 11PM and IDGAF)
thumb_file=InMemoryUploadedFile(thumb_io,
None, thumb_name, ‘image/jpeg’, thumb_io.len, None)
10. Finally, you can upload your simulated file
(and your original full-size file).
Remember to catch the url:
upload_path=upload(new_upload)
thumb_path=upload(thumb_file)
11. Finally finally, create a new instance of the
model that will store the data. In my
case, the model is called Cat_pictures
new_pic=Cat_pictures()
(in
models.py):
class
Cat_pictures(models.Model):
url=models.TextField(blank=True)
thumb_path=models.TextField(blank=True)
the blank=True argument allows users to
submit a new sighting without submitting a photo if wanted.
in
views.py:
new_pic.url=upload_path
new_pic.thumb_path=thumb_path
12. Don’t forget to save the new instance.
new_pic.save()