Skip to content

Commit 59ed733

Browse files
authored
support text direct downloads (#490)
## 合入说明 - 修复 `GET /share/select/` 文本分享直链错误走存储层导致“已删除/过期”的问题 - 文本分享返回 UTF-8 plain text 附件下载 - 补充回归测试,不影响现有文件下载与次数扣减逻辑 ## 评估结论 有必要合入:master 仍复现该缺陷,改动小、无冲突、测试通过。
2 parents cf6d364 + f2277e1 commit 59ed733

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

apps/base/views.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
import os
55
import uuid
66
from datetime import timedelta
7-
from urllib.parse import unquote
7+
from urllib.parse import quote, unquote
88

99
from typing import Optional, Tuple, Union
1010

1111
from fastapi import APIRouter, Form, Request, UploadFile, File, Depends, HTTPException
1212
from pydantic import BaseModel, ValidationError
1313
from starlette import status
14+
from starlette.responses import Response
1415
from tortoise.expressions import Case, F, Q, When
1516

1617
from apps.admin.dependencies import share_required_login
@@ -334,6 +335,17 @@ async def get_code_file(code: str, ip: str = Depends(ip_limit["error"])):
334335
assert isinstance(file_code, FileCodes)
335336
if not await consume_file_usage(file_code):
336337
return APIResponse(code=404, detail="文件已过期")
338+
if file_code.text is not None:
339+
filename = f"{file_code.prefix or 'Text'}{file_code.suffix or '.txt'}"
340+
return Response(
341+
content=file_code.text,
342+
media_type="text/plain",
343+
headers={
344+
"Content-Disposition": (
345+
f"attachment; filename*=UTF-8''{quote(filename, safe='')}"
346+
)
347+
},
348+
)
337349
return await file_storage.get_file_response(file_code)
338350

339351

tests/test_issue_482_share_usage.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ async def _run_scenario(self):
7171
await self._assert_count_consumption_is_atomic()
7272
await self._assert_time_expiration_and_usage_are_atomic()
7373
await self._assert_download_url_is_single_use()
74+
await self._assert_text_share_downloads_as_txt()
7475
await self._assert_limited_files_do_not_expose_direct_urls()
7576
await self._assert_metadata_counts_all_attempts()
7677
finally:
@@ -156,6 +157,30 @@ async def _assert_download_url_is_single_use(self):
156157
self.assertEqual(record.expired_count, 0)
157158
self.assertEqual(record.used_count, 1)
158159

160+
async def _assert_text_share_downloads_as_txt(self):
161+
record = await FileCodes.create(
162+
code="text-download",
163+
prefix="TAG",
164+
text="hello, 文本直链",
165+
expired_count=-1,
166+
expired_at=None,
167+
)
168+
169+
with patch.dict(views.storages, {"local": FakeStorage}):
170+
response = await views.get_code_file(
171+
code=record.code, ip="127.0.0.1"
172+
)
173+
174+
self.assertEqual(response.status_code, 200)
175+
self.assertEqual(response.body.decode("utf-8"), record.text)
176+
self.assertEqual(response.media_type, "text/plain")
177+
self.assertEqual(
178+
response.headers["content-disposition"],
179+
"attachment; filename*=UTF-8''TAG.txt",
180+
)
181+
await record.refresh_from_db()
182+
self.assertEqual(record.used_count, 1)
183+
159184
async def _assert_limited_files_do_not_expose_direct_urls(self):
160185
record = await FileCodes.create(
161186
code="proxy-only",

0 commit comments

Comments
 (0)