Don't load temporary uploaded files into memory

These can be quite large. Instead pass them straight to Willow to have it read just as much as it needs to
pull/10310/head
Jake Howard 2023-03-10 12:00:46 +00:00 zatwierdzone przez Matt Westcott
rodzic 3c0c64642b
commit cfa11bbe00
2 zmienionych plików z 43 dodań i 5 usunięć

Wyświetl plik

@ -146,11 +146,11 @@ class WagtailImageField(ImageField):
if f is None: if f is None:
return None return None
# We need to get a file object for Pillow. When we get a path, we need to open # Get the file content ready for Willow
# the file first. And we have to read the data into memory to pass to Willow.
if hasattr(data, "temporary_file_path"): if hasattr(data, "temporary_file_path"):
with open(data.temporary_file_path(), "rb") as fh: # Django's `TemporaryUploadedFile` is enough of a file to satisfy Willow
file = BytesIO(fh.read()) # Willow doesn't support opening images by path https://github.com/wagtail/Willow/issues/108
file = data
else: else:
if hasattr(data, "read"): if hasattr(data, "read"):
file = BytesIO(data.read()) file = BytesIO(data.read())

Wyświetl plik

@ -3,7 +3,7 @@ import json
import urllib import urllib
from django.contrib.auth.models import Group, Permission from django.contrib.auth.models import Group, Permission
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile, TemporaryUploadedFile
from django.template.defaultfilters import filesizeformat from django.template.defaultfilters import filesizeformat
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.test import RequestFactory, TestCase, override_settings from django.test import RequestFactory, TestCase, override_settings
@ -567,6 +567,44 @@ class TestImageAddView(WagtailTestUtils, TestCase):
images = Image.objects.filter(title="Test image") images = Image.objects.filter(title="Test image")
self.assertEqual(images.count(), 1) self.assertEqual(images.count(), 1)
def test_add_temporary_uploaded_file(self):
"""
Test that uploading large files (spooled to the filesystem) work as expected
"""
test_image_file = get_test_image_file()
uploaded_file = TemporaryUploadedFile(
"test.png", "image/png", test_image_file.size, "utf-8"
)
uploaded_file.write(test_image_file.file.getvalue())
uploaded_file.seek(0)
response = self.post(
{
"title": "Test image",
"file": uploaded_file,
}
)
# Should redirect back to index
self.assertRedirects(response, reverse("wagtailimages:index"))
# Check that the image was created
images = Image.objects.filter(title="Test image")
self.assertEqual(images.count(), 1)
# Test that size was populated correctly
image = images.first()
self.assertEqual(image.width, 640)
self.assertEqual(image.height, 480)
# Test that the file_size/hash fields were set
self.assertTrue(image.file_size)
self.assertTrue(image.file_hash)
# Test that it was placed in the root collection
root_collection = Collection.get_first_root_node()
self.assertEqual(image.collection, root_collection)
@override_settings( @override_settings(
DEFAULT_FILE_STORAGE="wagtail.test.dummy_external_storage.DummyExternalStorage" DEFAULT_FILE_STORAGE="wagtail.test.dummy_external_storage.DummyExternalStorage"
) )