Repository: wagtail/wagtail

Issue:
template Image based error
value too long for type character varying(100) image

Happens when we uploaded an image with a filename containing filter information in its filename and is displayed in a template, (we think) if template is trying to render it with filter information of dimensions bigger than it's physical size.

Also happened in image admin at one point, and had to reset the db. 

the image filename was Victoria-Parsons.focus-3618-1640-4557x3146.max-900x682.jpg

if the template tried to render it with  
 {% image carousel_item.image fill-900x600 as carouselimagedata %} it was fine, but when we wanted a bigger version in our template we used 
               {% image carousel_item.image fill-1000x600 as carouselimagedata %} 
and the error appeared when trying to access the page with that image.

to make sure the image wasn't corrupt, we renamed a different image with the same filename, and it happened again. We also tried smaller images than the new bigger dimensions, and they didn't cause an error.



Discussion:
@jossingram thanks for spotting. I have been able to reproduce this issue.

The issue is that the renditions filename is being truncated before adding the focal point key to it. So a long focal point key with a long original filename can cause the final renditions filename to go over the 100 character limit causing the crash.

It was implemented this way to prevent the focal point key from being chopped off by the truncation step (to prevent filename conflits). This is probably not needed though as I think Django will resolve filename conflicts for us anyway.

No worries. Should we avoid using long filenames for now then?

Yep, that will prevent the crash from happening. I'll get a proper fix for it out soon.

Cool. thanks


Resolving patch:
diff --git a/wagtail/wagtailimages/models.py b/wagtail/wagtailimages/models.py
--- a/wagtail/wagtailimages/models.py
+++ b/wagtail/wagtailimages/models.py
@@ -161,10 +161,9 @@ def get_rendition(self, filter):
 
             input_filename_parts = os.path.basename(file_field.file.name).split('.')
             filename_without_extension = '.'.join(input_filename_parts[:-1])
-            filename_without_extension = filename_without_extension[:60]  # trim filename base so that we're well under 100 chars
-            output_filename_parts = [filename_without_extension, focal_point_key, filter.spec] + input_filename_parts[-1:]
-            output_filename = '.'.join(output_filename_parts)
-
+            extension = '.'.join([focal_point_key, filter.spec] + input_filename_parts[-1:])
+            filename_without_extension = filename_without_extension[:(59-len(extension))] # Truncate filename to prevent it going over 60 chars
+            output_filename = filename_without_extension + '.' + extension
             generated_image_file = File(generated_image, name=output_filename)
 
             if self.focal_point:


Test patch:
diff --git a/wagtail/wagtailimages/tests.py b/wagtail/wagtailimages/tests.py
--- a/wagtail/wagtailimages/tests.py
+++ b/wagtail/wagtailimages/tests.py
@@ -32,7 +32,7 @@
 from wagtail.wagtailcore.models import Page
 
 
-def get_test_image_file():
+def get_test_image_file(filename='test.png'):
     from six import BytesIO
     from PIL import Image
     from django.core.files.images import ImageFile
@@ -40,7 +40,7 @@ def get_test_image_file():
     f = BytesIO()
     image = Image.new('RGB', (640, 480), 'white')
     image.save(f, 'PNG')
-    return ImageFile(f, name='test.png')
+    return ImageFile(f, name=filename)
 
 
 Image = get_image_model()
@@ -1016,3 +1016,24 @@ def test_keeps_focal_point_in_view_top_right(self):
             CropBox(125, 25, 275, 175),
         )
 
+
+class TestIssue573(TestCase):
+    """
+    This tests for a bug which causes filename limit on Renditions to be reached
+    when the Image has a long original filename and a big focal point key
+    """
+    def test_issue_573(self):
+        # Create an image with a big filename and focal point
+        image = Image.objects.create(
+            title="Test image",
+            file=get_test_image_file('thisisaverylongfilename-abcdefghijklmnopqrstuvwxyz-supercalifragilisticexpialidocious.png'),
+            focal_point_x=1000,
+            focal_point_y=1000,
+            focal_point_width=1000,
+            focal_point_height=1000,
+        )
+
+        # Try creating a rendition from that image
+        # This would crash if the bug is present
+        image.get_rendition('fill-800x600')
+