Reputation: 1009
I'm building a django site, and when I upload an image (the model is Photo) through the admin view, I want to generate an arbitrary thumbnail of it and save that off to a Thumbnail Image Field. The thumbnails are generating nicely, but when the Photo model tries to save, I get that I've tried to perform an I/O operation on an object that's closed: "I/O operation on closed file". The stack trace traces back up to the call to the superclass, so I think it's made it through the generate_thumbnail function.
What am I doing wrong? Any help would be appreciated.
from PIL import Image
from io import BytesIO
from django.core.files.base import ContentFile
class Photo(models.Model):
photo=models.ImageField(upload_to='photos/',blank=True)
thumbnail = models.ImageField(upload_to='thumbs/', editable=False, null=True)
def save(self,*args,**kwargs):
if self.photo:
self.generate_thumbnail()
super(Photo, self).save(*args,**kwargs)
def generate_thumbnail(self):
img =Image.open(self.photo)
img.thumbnail([consts.THUMB_HEIGHT,consts.THUMB_WIDTH],Image.ANTIALIAS)
name = os.path.basename(self.photo.name)
thumb_name, thumb_ext = os.path.splitext(name)
thumb_ext=thumb_ext.lower()
outname="thumb_"+thumb_name+thumb_ext
if thumb_ext in ['.jpg','.jpeg']:
filetype='JPEG'
elif thumb_ext == '.gif':
filetype='GIF'
elif thumb_ext=='.png':
filetype = 'PNG'
else:
raise Exception("Failed to generate thumbnail. Is the filetype valid?")
temp_thumb=BytesIO()
img.save(temp_thumb,filetype)
temp_thumb.seek(0)
self.thumbnail.save(outname,ContentFile(temp_thumb.read()),save=False)
temp_thumb.close()
return True
So, a day later, same code, different error. Now it's file not found with a file in a temp directory. The stack trace for this error is as follows.
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/admin/coffins/photo/11/change/
Django Version: 2.1.5
Python Version: 3.7.2
Installed Applications:
['storages',
'coffins.apps.CoffinsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "/blah/venv/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
34. response = get_response(request)
File "/blah/venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
126. response = self.process_exception_by_middleware(e, request)
File "/blah/venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
124. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/blah/venv/lib/python3.7/site-packages/django/contrib/admin/options.py" in wrapper
604. return self.admin_site.admin_view(view)(*args, **kwargs)
File "/blah/venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapped_view
142. response = view_func(request, *args, **kwargs)
File "/blah/venv/lib/python3.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
44. response = view_func(request, *args, **kwargs)
File "/blah/venv/lib/python3.7/site-packages/django/contrib/admin/sites.py" in inner
223. return view(request, *args, **kwargs)
File "/blah/venv/lib/python3.7/site-packages/django/contrib/admin/options.py" in change_view
1640. return self.changeform_view(request, object_id, form_url, extra_context)
File "/blah/venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
45. return bound_method(*args, **kwargs)
File "/blah/venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapped_view
142. response = view_func(request, *args, **kwargs)
File "/blah/venv/lib/python3.7/site-packages/django/contrib/admin/options.py" in changeform_view
1525. return self._changeform_view(request, object_id, form_url, extra_context)
File "/blah/venv/lib/python3.7/site-packages/django/contrib/admin/options.py" in _changeform_view
1564. self.save_model(request, new_object, form, not add)
File "/blah/venv/lib/python3.7/site-packages/django/contrib/admin/options.py" in save_model
1091. obj.save()
File "/blah/coffins/models.py" in save
62. super(Photo, self).save(*args,**kwargs)
File "/blah/venv/lib/python3.7/site-packages/django/db/models/base.py" in save
718. force_update=force_update, update_fields=update_fields)
File "/blah/venv/lib/python3.7/site-packages/django/db/models/base.py" in save_base
748. updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/blah/venv/lib/python3.7/site-packages/django/db/models/base.py" in _save_table
809. for f in non_pks]
File "/blah/venv/lib/python3.7/site-packages/django/db/models/base.py" in <listcomp>
809. for f in non_pks]
File "/blah/venv/lib/python3.7/site-packages/django/db/models/fields/files.py" in pre_save
288. file.save(file.name, file.file, save=False)
File "/blah/venv/lib/python3.7/site-packages/django/db/models/fields/files.py" in save
87. self.name = self.storage.save(name, content, max_length=self.field.max_length)
File "/blah/venv/lib/python3.7/site-packages/django/core/files/storage.py" in save
49. return self._save(name, content)
File "/blah/venv/lib/python3.7/site-packages/django/core/files/storage.py" in _save
255. file_move_safe(content.temporary_file_path(), full_path)
File "/blah/venv/lib/python3.7/site-packages/django/core/files/move.py" in file_move_safe
56. with open(old_file_name, 'rb') as old_file:
Exception Type: FileNotFoundError at /admin/coffins/photo/11/change/
Exception Value: [Errno 2] No such file or directory: '/var/folders/s6/w4dbxvkj0ng4l7s5_pqd_mxr0000gn/T/tmpunp8vg4b.upload.jpg'
Upvotes: 0
Views: 494
Reputation: 1009
My solution to this was ultimately to download a library that did it: stdimage does exactly what I wanted this to do--it builds a thumbnail of coder-defined size at the time of picture upload and saves that to whatever the app's storage is--in my case, S3. Most of the libraries out there seem to generate the thumbnail on demand, and the thumbnail is specified in the template, not the model. This generally required some sort of memcaching. For my needs, this was not optimal. Having looked at the code for this plugin, they cover a lot of edgecases that I wasn't dealing with here, and would not have been willing to put in the time to cover on my own.
Thanks to everyone who helped!
Upvotes: 1