Skip to content

Commit

Permalink
Fix open files leak when working with ImageWithThumb or Thumbnail filter
Browse files Browse the repository at this point in the history
  • Loading branch information
amol- committed Feb 21, 2024
1 parent f9a5d9e commit f1372fb
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 40 deletions.
39 changes: 21 additions & 18 deletions depot/fields/filters/thumbnails.py
Expand Up @@ -31,21 +31,24 @@ def __init__(self, size=(128,128), format='PNG'):
self.thumbnail_format = format

def on_save(self, uploaded_file):
content = utils.file_from_content(uploaded_file.original_content)

thumbnail = Image.open(content)
thumbnail.thumbnail(self.thumbnail_size, Image.BILINEAR)
thumbnail = thumbnail.convert('RGBA')
thumbnail.format = self.thumbnail_format

output = BytesIO()
thumbnail.save(output, self.thumbnail_format)
output.seek(0)

thumb_name = 'thumb_%sx%s' % self.thumbnail_size
thumb_file_name = '%s.%s' % (thumb_name, self.thumbnail_format.lower())
thumb_path, thumb_id = uploaded_file.store_content(output, thumb_file_name)
uploaded_file[thumb_name + '_id'] = thumb_id
uploaded_file[thumb_name + '_path'] = thumb_path
uploaded_file[thumb_name + '_url'] = DepotManager.get_middleware().url_for(thumb_path)

close_content, content = utils.file_from_content(uploaded_file.original_content)

try:
thumbnail = Image.open(content)
thumbnail.thumbnail(self.thumbnail_size, Image.BILINEAR)
thumbnail = thumbnail.convert('RGBA')
thumbnail.format = self.thumbnail_format

output = BytesIO()
thumbnail.save(output, self.thumbnail_format)
output.seek(0)

thumb_name = 'thumb_%sx%s' % self.thumbnail_size
thumb_file_name = '%s.%s' % (thumb_name, self.thumbnail_format.lower())
thumb_path, thumb_id = uploaded_file.store_content(output, thumb_file_name)
uploaded_file[thumb_name + '_id'] = thumb_id
uploaded_file[thumb_name + '_path'] = thumb_path
uploaded_file[thumb_name + '_url'] = DepotManager.get_middleware().url_for(thumb_path)
finally:
if close_content:
content.close()
44 changes: 25 additions & 19 deletions depot/fields/specialized/image.py
Expand Up @@ -29,34 +29,40 @@ class UploadedImageWithThumb(UploadedFile):

def process_content(self, content, filename=None, content_type=None):
orig_content = content
content = utils.file_from_content(content)
temporary_content = False
__, filename, content_type = FileStorage.fileinfo(orig_content)

uploaded_image = Image.open(content)
if max(uploaded_image.size) >= self.max_size:
uploaded_image.thumbnail((self.max_size, self.max_size), Image.BILINEAR)
content = SpooledTemporaryFile(INMEMORY_FILESIZE)
uploaded_image.save(content, uploaded_image.format)

content.seek(0)
super(UploadedImageWithThumb, self).process_content(content, filename, content_type)
close_content, content = utils.file_from_content(content)

try:
uploaded_image = Image.open(content)
if max(uploaded_image.size) >= self.max_size:
uploaded_image.thumbnail((self.max_size, self.max_size), Image.BILINEAR)
content = SpooledTemporaryFile(INMEMORY_FILESIZE)
close_content = True
uploaded_image.save(content, uploaded_image.format)

content.seek(0)
super(UploadedImageWithThumb, self).process_content(content, filename, content_type)
finally:
if close_content:
content.close()

thumbnail = uploaded_image.copy()
thumbnail.thumbnail(self.thumbnail_size, Image.LANCZOS)
thumbnail = thumbnail.convert('RGBA')
thumbnail.format = self.thumbnail_format

output = SpooledTemporaryFile(INMEMORY_FILESIZE)
thumbnail.save(output, self.thumbnail_format)
output.seek(0)
with SpooledTemporaryFile(INMEMORY_FILESIZE) as output:
thumbnail.save(output, self.thumbnail_format)
output.seek(0)

thumb_path, thumb_id = self.store_content(output,
'thumb.%s' % self.thumbnail_format.lower())
self['thumb_id'] = thumb_id
self['thumb_path'] = thumb_path
thumb_path, thumb_id = self.store_content(output,
'thumb.%s' % self.thumbnail_format.lower())
self['thumb_id'] = thumb_id
self['thumb_path'] = thumb_path

thumbnail_file = self.thumb_file
self['_thumb_public_url'] = thumbnail_file.public_url
thumbnail_file = self.thumb_file
self['_thumb_public_url'] = thumbnail_file.public_url

@property
def thumb_file(self):
Expand Down
4 changes: 3 additions & 1 deletion depot/io/utils.py
Expand Up @@ -18,16 +18,18 @@ def file_from_content(content):
Converts ``FileStorage``, ``FileIntent`` and
``bytes`` to an actual file.
"""
must_close = False
f = content
if isinstance(content, FileIntent):
f = content._fileobj
elif isinstance(content, byte_string):
must_close = True
f = SpooledTemporaryFile(INMEMORY_FILESIZE)
f.write(content)
elif _is_fieldstorage_like(content):
f = content.file
f.seek(0)
return f
return must_close, f


class FileIntent(object):
Expand Down
7 changes: 5 additions & 2 deletions docs/database.rst
Expand Up @@ -129,7 +129,7 @@ A filter that creates a thumbnail for an image would look like::
self.thumbnail_format = format

def on_save(self, uploaded_file):
content = utils.file_from_content(uploaded_file.original_content)
close_content, content = utils.file_from_content(uploaded_file.original_content)

thumbnail = Image.open(content)
thumbnail.thumbnail(self.thumbnail_size, Image.BILINEAR)
Expand All @@ -140,6 +140,9 @@ A filter that creates a thumbnail for an image would look like::
thumbnail.save(output, self.thumbnail_format)
output.seek(0)

if close_content:
content.close()

thumb_file_name = 'thumb.%s' % self.thumbnail_format.lower()

# If you upload additional files do it with store_content
Expand Down Expand Up @@ -208,7 +211,7 @@ a maximum resolution::
__, filename, content_type = FileStorage.fileinfo(content)

# Get a file object even if content was bytes
content = utils.file_from_content(content)
_, content = utils.file_from_content(content)

uploaded_image = Image.open(content)
if max(uploaded_image.size) >= self.max_size:
Expand Down

0 comments on commit f1372fb

Please sign in to comment.