Use filename when downloading asset through UI (#1146)

This commit is contained in:
Per Mortensen
2025-08-10 08:38:18 +02:00
committed by GitHub
parent 5330252db9
commit 93faf70b37
4 changed files with 53 additions and 11 deletions

View File

@@ -199,13 +199,10 @@ class BookmarkAssetViewSet(
if asset.gzip if asset.gzip
else open(file_path, "rb") else open(file_path, "rb")
) )
file_name = (
f"{asset.display_name}.html"
if asset.asset_type == BookmarkAsset.TYPE_SNAPSHOT
else asset.display_name
)
response = StreamingHttpResponse(file_stream, content_type=content_type) response = StreamingHttpResponse(file_stream, content_type=content_type)
response["Content-Disposition"] = f'attachment; filename="{file_name}"' response["Content-Disposition"] = (
f'attachment; filename="{asset.download_name}"'
)
return response return response
except FileNotFoundError: except FileNotFoundError:
raise Http404("Asset file does not exist") raise Http404("Asset file does not exist")

View File

@@ -133,6 +133,14 @@ class BookmarkAsset(models.Model):
status = models.CharField(max_length=64, blank=False, null=False) status = models.CharField(max_length=64, blank=False, null=False)
gzip = models.BooleanField(default=False, null=False) gzip = models.BooleanField(default=False, null=False)
@property
def download_name(self):
return (
f"{self.display_name}.html"
if self.asset_type == BookmarkAsset.TYPE_SNAPSHOT
else self.display_name
)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.file: if self.file:
try: try:

View File

@@ -4,9 +4,8 @@ from django.conf import settings
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from bookmarks.tests.helpers import ( from bookmarks.models import BookmarkAsset
BookmarkFactoryMixin, from bookmarks.tests.helpers import BookmarkFactoryMixin
)
class BookmarkAssetViewTestCase(TestCase, BookmarkFactoryMixin): class BookmarkAssetViewTestCase(TestCase, BookmarkFactoryMixin):
@@ -23,7 +22,21 @@ class BookmarkAssetViewTestCase(TestCase, BookmarkFactoryMixin):
def setup_asset_with_file(self, bookmark): def setup_asset_with_file(self, bookmark):
filename = f"temp_{bookmark.id}.html.gzip" filename = f"temp_{bookmark.id}.html.gzip"
self.setup_asset_file(filename) self.setup_asset_file(filename)
asset = self.setup_asset(bookmark=bookmark, file=filename) asset = self.setup_asset(
bookmark=bookmark, file=filename, display_name=f"Snapshot {bookmark.id}"
)
return asset
def setup_asset_with_uploaded_file(self, bookmark):
filename = f"temp_{bookmark.id}.png.gzip"
self.setup_asset_file(filename)
asset = self.setup_asset(
bookmark=bookmark,
file=filename,
asset_type=BookmarkAsset.TYPE_UPLOAD,
content_type="image/png",
display_name=f"Uploaded file {bookmark.id}.png",
)
return asset return asset
def view_access_test(self, view_name: str): def view_access_test(self, view_name: str):
@@ -127,3 +140,25 @@ class BookmarkAssetViewTestCase(TestCase, BookmarkFactoryMixin):
def test_reader_view_access_guest_user(self): def test_reader_view_access_guest_user(self):
self.view_access_guest_user_test("linkding:assets.read") self.view_access_guest_user_test("linkding:assets.read")
def test_snapshot_download_name(self):
bookmark = self.setup_bookmark()
asset = self.setup_asset_with_file(bookmark)
response = self.client.get(reverse("linkding:assets.view", args=[asset.id]))
self.assertEqual(response["Content-Type"], asset.content_type)
self.assertEqual(
response["Content-Disposition"],
f'inline; filename="{asset.display_name}.html"',
)
def test_uploaded_file_download_name(self):
bookmark = self.setup_bookmark()
asset = self.setup_asset_with_uploaded_file(bookmark)
response = self.client.get(reverse("linkding:assets.view", args=[asset.id]))
self.assertEqual(response["Content-Type"], asset.content_type)
self.assertEqual(
response["Content-Disposition"],
f'inline; filename="{asset.display_name}"',
)

View File

@@ -31,7 +31,9 @@ def view(request, asset_id: int):
asset = access.asset_read(request, asset_id) asset = access.asset_read(request, asset_id)
content = _get_asset_content(asset) content = _get_asset_content(asset)
return HttpResponse(content, content_type=asset.content_type) response = HttpResponse(content, content_type=asset.content_type)
response["Content-Disposition"] = f'inline; filename="{asset.download_name}"'
return response
def read(request, asset_id: int): def read(request, asset_id: int):