Opened 4 weeks ago
Closed 2 weeks ago
#36975 closed Bug (duplicate)
SimpleUploadedFile cannot be re-opened
| Reported by: | Denis Washington | Owned by: | Vishy Algo |
|---|---|---|---|
| Component: | File uploads/storage | Version: | 6.0 |
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Summary
When a SimpleUploadedFile is closed, it calls close() on the underlying BytesIO, which means that the next call to open() fails (ValueError: I/O operation on closed file.). This is unlike the conceptually similar ContentFile, which explicitly overrides close() to do nothing, avoiding this issue.
How to Reproduce
The following script reproduces the issue:
from django.core.files.base import ContentFile from django.core.files.uploadedfile import SimpleUploadedFile def read_twice(file): with file.open() as f: print(f.read()) with file.open() as f: print(f.read()) # Works as expected read_twice(ContentFile(b"test data")) # ValueError: I/O operation on closed file. read_twice(SimpleUploadedFile("test.txt", b"test data"))
Expected Behavior
SimpleUploadedFile should follow ContentFile in making close() a no-op.
Ideally, the same should be done by its superclass InMemoryUploadedFile, at least if its file is a BytesIO or StringIO. (Skipping closing unconditionally could be risky here because the init method accepts any file-like object in principle.)
Change History (5)
comment:1 by , 4 weeks ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:2 by , 3 weeks ago
comment:3 by , 3 weeks ago
| Owner: | changed from to |
|---|
comment:4 by , 3 weeks ago
| Component: | Uncategorized → Core (Other) |
|---|
comment:5 by , 2 weeks ago
| Component: | Core (Other) → File uploads/storage |
|---|---|
| Resolution: | → duplicate |
| Status: | assigned → closed |
| Type: | Uncategorized → Bug |
Thanks, I'm a little sympathetic to your report, since it seems like a straightforward violation of the docs for File.open(), which claim it handles re-opening, and shouldn't be complicated to fix, but I'm going to respect the prior triage decision here in #33023. Feel free to start a forum discussion if you'd like to gather consensus to revisit.
This appears to be a fundamental file handling problem: once a
BytesIOobject is created and passed to parent classes, it cannot be reconstructed if abruptly closed. Since it's anInMemoryUploadedFile, we could save the content and recreate the file object viaopen(), allowing the context manager to clean it up automatically.But, currently
InMemoryUploadedFileaccepts file object while construction, we have to add it to theSimpleUploadedFilesomewhat similar to this,django/core/files/uploadedfile.py
content = content or b""content), None, name, content_type, len(content), None, NoneI've verified this change with a unit test, and it works as expected. I believe using
InMemoryUploadedFilewould have been more appropriate thanSimpleUploadedFile, as it enables easy reconstruction of the file from its content throughout the request lifecycle for all in-memory file wrappers.I'd appreciate any second opinions or suggestions on this approach.