matthewwithanm / django-imagekit

Automated image processing for Django. Currently v4.0
http://django-imagekit.rtfd.org/
BSD 3-Clause "New" or "Revised" License
2.26k stars 276 forks source link

Tiff compression option #486

Open niggle opened 5 years ago

niggle commented 5 years ago

When i try to use option compression for tiff images, raises an error

ProcessedImageField(upload_to=upload_path_handler,
                                      processors=[Adjust(color=0)],
                                      format='TIFF',
                                      options={'tiffinfo': {317: 2}, 'compression': "tiff_lzw"},
                                      null=True)

error:

AttributeError at /cabinet/1/company/1/documents/upload/web/
No exception message supplied

Traceback:  

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/pilkit/utils.py" in fileno
  150.             return self._wrapped.fileno()

      During handling of the above exception (fileno), another exception occurred:

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "/home/marcio/dev/neoscan/documents/mixins.py" in dispatch
  15.         return super().dispatch(request, *args, **kwargs)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "/home/marcio/dev/neoscan/documents/views.py" in post
  64.                                     scan=item, scan_thumbnail=item, scan_preview=item, nr=index+1)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/manager.py" in manager_method
  85.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/query.py" in create
  394.         obj.save(force_insert=True, using=self.db)

File "/home/marcio/dev/neoscan/documents/models.py" in save
  192.         super().save(*args, **kwargs)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/base.py" in save
  808.                        force_update=force_update, update_fields=update_fields)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/base.py" in save_base
  838.             updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/base.py" in _save_table
  924.             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/base.py" in _do_insert
  963.                                using=using, raw=raw)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/manager.py" in manager_method
  85.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/query.py" in _insert
  1079.         return query.get_compiler(using=using).execute_sql(return_id)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in execute_sql
  1111.             for sql, params in self.as_sql():

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in as_sql
  1064.                 for obj in self.query.objs

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in <listcomp>
  1064.                 for obj in self.query.objs

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in <listcomp>
  1063.                 [self.prepare_value(field, self.pre_save_val(field, obj)) for field in fields]

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in pre_save_val
  1013.         return field.pre_save(obj, add=True)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/django/db/models/fields/files.py" in pre_save
  296.             file.save(file.name, file.file, save=False)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/imagekit/models/fields/files.py" in save
  12.         content = generate(spec)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/imagekit/utils.py" in generate
  152.     content = generator.generate()

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/imagekit/specs/__init__.py" in generate
  161.                                       options=self.options)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/pilkit/utils.py" in process_image
  364.     return img_to_fobj(img, format, autoconvert, **options)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/pilkit/utils.py" in img_to_fobj
  17.     return save_image(img, StringIO(), format, options, autoconvert)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/pilkit/utils.py" in save_image
  203.         save(wrapper)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/pilkit/utils.py" in save
  191.             img.save(fp, format, **options)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/PIL/Image.py" in save
  1994.             save_handler(self, fp, filename)

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/PIL/TiffImagePlugin.py" in _save
  1503.                 _fp = os.dup(fp.fileno())

File "/home/marcio/.virtualenvs/neoscan/lib/python3.6/site-packages/pilkit/utils.py" in fileno
  152.             raise AttributeError

Exception Type: AttributeError at /cabinet/1/company/1/documents/upload/web/

Request information:
USER: admin@admin.pt

GET: No GET data

POST:
document_name = '06022019_11463610'
book = ''
csrfmiddlewaretoken = 'w1qbCtJ8pHvswVYxj2hWhAUykdnIQLqeEb2ecJZwrAuLs5uNWBoF6CCfSPfF9T5i'

FILES:
page = <InMemoryUploadedFile: shopify.jpg (image/jpeg)>

any idea of how can i solve this??

vstoykov commented 5 years ago

The whole backtrace is missing but from what I can see in the pilkit's FileWrapper it looks like that the file that is wrapped is not actual file but something file like which does not have fileno. I'm not completely why we catch UnsupportedOperation and raise AttributeError instead.

You said that the option to compress images is the problem right? Which means that if you remove the options completely it will work right?

niggle commented 5 years ago

sorry for the missing backtrace. yes only when i put compression inside options. if i remove compression it works great, also works if i remove all options.

if i try to make the compression directly with pillow also works fine.

later i'll put the all backtrace.

vstoykov commented 5 years ago

In the traceback we can see that the problem is caused in pilkit.utils.img_to_fobj by passing StringIO (actually BytesIO in Python3 but called StringIO for legacy reasons - Python2) instead of real file which has fileno method. The Tiff plugin in PIL is requiring the file to be real (to have fileno method). This can be considered bug in pilkit or bug in PIL/PIllow. Usage of the fileno in PIL/Pillow probably is for performance reasons but probably there should be a workaround for non real files (file like objects) for StringIO/BytesIO.