Rokujolady
Rokujolady

Reputation: 1009

I/O on Closed File error in Django When generating thumbnail of uploaded image

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

Answers (1)

Rokujolady
Rokujolady

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

Related Questions