This commit is contained in:
2026-07-28 21:42:58 +10:00
parent 21d4bd5f47
commit e5da25bb01
2 changed files with 42 additions and 1 deletions
+9 -1
View File
@@ -74,7 +74,15 @@ def _save_uploaded_file(f):
os.rename(tmp_path, dest_path)
mime_type = f.content_type or mimetypes.guess_type(f.name)[0] or 'application/octet-stream'
guessed = mimetypes.guess_type(f.name)[0]
# Clients (notably the iOS uploader) often send a generic
# "application/octet-stream" Content-Type even for media files. When that
# happens, prefer the type inferred from the filename extension so the
# file is previewable/streamable in the UI.
if f.content_type and f.content_type != 'application/octet-stream':
mime_type = f.content_type
else:
mime_type = guessed or f.content_type or 'application/octet-stream'
return FileUpload.objects.create(
name=f.name,
stored_name=stored_name,
+33
View File
@@ -27,6 +27,21 @@ def _pdf():
return SimpleUploadedFile("document.pdf", data, content_type="application/pdf")
def _mp4_generic_content_type():
"""An .mp4 upload whose client sent a generic Content-Type.
Reproduces the iOS-uploader behaviour that caused mb4 files to be
persisted as "application/octet-stream" and therefore un-previewable
in the UI. The server must infer "video/mp4" from the filename instead.
"""
# minimal mp4 box header — content is irrelevant, only the extension
# and Content-Type matter for the mime-type inference path.
data = b"\x00\x00\x00\x20ftypisom\x00\x00\x02\x00isomiso2avc1mp41"
return SimpleUploadedFile(
"clip.mp4", data, content_type="application/octet-stream"
)
@pytest.mark.django_db
class TestFilesAPI:
BASE = "/api/files"
@@ -171,3 +186,21 @@ class TestFilesAPI:
).json()[0]
assert "formatted_size" in uploaded
assert "B" in uploaded["formatted_size"]
def test_upload_infers_mime_from_filename_when_client_sends_generic(self, api_client):
"""An .mp4 uploaded with Content-Type=application/octet-stream must
still be persisted as video/mp4 so the UI can render a <video>."""
uploaded = api_client.post(
f"{self.BASE}", {"files": _mp4_generic_content_type()}, format="multipart"
).json()[0]
assert uploaded["mime_type"] == "video/mp4"
def test_download_serves_inline_for_generic_content_type_mp4(self, client, api_client):
uploaded = api_client.post(
f"{self.BASE}", {"files": _mp4_generic_content_type()}, format="multipart"
).json()[0]
pk, name = uploaded["id"], uploaded["name"]
r = client.get(f"/ui/files/{pk}-{name}")
assert r.status_code == 200
assert r["Content-Type"] == "video/mp4"
assert (r["Content-Disposition"] or "").startswith("inline"), r["Content-Disposition"]