Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions fs_attachment/models/ir_binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,17 @@ def _get_stream_from(
if stream.type == "fs":
if mimetype:
stream.mimetype = mimetype
elif not stream.mimetype:
stream.mimetype = default_mimetype
if filename:
stream.download_name = filename
elif record and filename_field in record:
stream.download_name = record[filename_field] or stream.download_name

if (
not get_extension(stream.download_name)
and stream.mimetype != "application/octet-stream"
):
stream.download_name += guess_extension(stream.mimetype) or ""
if (
not get_extension(stream.download_name)
and stream.mimetype != "application/octet-stream"
):
stream.download_name += guess_extension(stream.mimetype) or ""

return stream

Expand Down
1 change: 1 addition & 0 deletions fs_attachment/readme/newsfragments/684.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a 500 error when downloading an attachment stored in a filesystem storage that has no mimetype: fall back to the default mimetype, as core does for attachments in the filestore.
48 changes: 48 additions & 0 deletions fs_attachment/tests/test_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,51 @@ def test_image_url_name_with_newline(self):
},
assert_content=self.image,
)

def _create_attachment_without_extension(self):
return (
self.env["ir.attachment"]
.with_context(storage_location=self.temp_backend.code)
.create({"name": "report", "raw": self.content})
)

def test_content_url_name_without_extension(self):
"""The extension is guessed from the mimetype for fs streams."""
attachment = self._create_attachment_without_extension()
self.assertEqual(attachment.mimetype, "text/plain")
self.authenticate("admin", "admin")
self.assertDownload(
f"/web/content/{attachment.id}",
headers={},
assert_status_code=200,
assert_headers={"Content-Disposition": "inline; filename=report.txt"},
assert_content=self.content,
)

def test_content_url_without_mimetype(self):
"""An fs attachment without mimetype can still be downloaded.

The mimetype can be empty for attachments written outside the ORM
(imports, migrations). As in core, it falls back to the default
mimetype instead of crashing.
"""
attachment = self._create_attachment_without_extension()
self.env.cr.execute(
"UPDATE ir_attachment SET mimetype = NULL WHERE id = %s", (attachment.id,)
)
attachment.invalidate_recordset(["mimetype"])
stream = self.env["ir.binary"]._get_stream_from(attachment)
self.assertEqual(stream.type, "fs")
self.assertEqual(stream.mimetype, "application/octet-stream")
self.assertEqual(stream.download_name, "report")
self.authenticate("admin", "admin")
self.assertDownload(
f"/web/content/{attachment.id}",
headers={},
assert_status_code=200,
assert_headers={
"Content-Type": "application/octet-stream",
"Content-Disposition": "inline; filename=report",
},
assert_content=self.content,
)
Loading