Skip to content

Commit 442ef5b

Browse files
committed
feat: 优化文件下载响应中的Content-Length获取逻辑
为所有存储后端添加文件大小获取逻辑,优先从实际存储系统获取文件大小,失败时回退到数据库记录的大小
1 parent 34e3694 commit 442ef5b

File tree

1 file changed

+70
-6
lines changed

1 file changed

+70
-6
lines changed

core/storage.py

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,19 @@ async def get_file_response(self, file_code: FileCodes):
144144
filename = f"{file_code.prefix}{file_code.suffix}"
145145
encoded_filename = quote(filename, safe='')
146146
content_disposition = f"attachment; filename*=UTF-8''{encoded_filename}"
147+
148+
# 优先使用文件系统大小
149+
content_length = file_code.size # 默认使用数据库中的大小
150+
try:
151+
content_length = file_path.stat().st_size
152+
except Exception:
153+
# 如果获取文件大小失败,继续使用默认大小
154+
pass
155+
147156
return FileResponse(
148157
file_path,
149158
media_type="application/octet-stream",
150-
headers={"Content-Disposition": content_disposition, "Content-Length": str(file_code.size)},
159+
headers={"Content-Disposition": content_disposition, "Content-Length": str(content_length)},
151160
filename=filename # 保留原始文件名以备某些场景使用
152161
)
153162

@@ -296,12 +305,29 @@ async def delete_file(self, file_code: FileCodes):
296305
async def get_file_response(self, file_code: FileCodes):
297306
try:
298307
filename = file_code.prefix + file_code.suffix
308+
content_length = file_code.size # 默认使用数据库中的大小
309+
299310
async with self.session.client(
300311
"s3",
301312
endpoint_url=self.endpoint_url,
302313
region_name=self.region_name,
303314
config=Config(signature_version=self.signature_version),
304315
) as s3:
316+
# 首先尝试获取文件大小(HEAD请求)
317+
try:
318+
head_response = await s3.head_object(
319+
Bucket=self.bucket_name,
320+
Key=await file_code.get_file_path()
321+
)
322+
# 从HEAD响应中获取Content-Length
323+
if 'ContentLength' in head_response:
324+
content_length = head_response['ContentLength']
325+
elif 'Content-Length' in head_response['ResponseMetadata']['HTTPHeaders']:
326+
content_length = int(head_response['ResponseMetadata']['HTTPHeaders']['Content-Length'])
327+
except Exception:
328+
# 如果HEAD请求失败,继续使用默认大小
329+
pass
330+
305331
link = await s3.generate_presigned_url(
306332
"get_object",
307333
Params={
@@ -330,7 +356,7 @@ async def stream_generator():
330356
from fastapi.responses import StreamingResponse
331357
headers = {
332358
"Content-Disposition": f'attachment; filename="{filename.encode("utf-8").decode("latin-1")}"',
333-
"Content-Length": str(file_code.size)
359+
"Content-Length": str(content_length)
334360
}
335361
return StreamingResponse(
336362
stream_generator(),
@@ -618,6 +644,18 @@ async def get_file_response(self, file_code: FileCodes):
618644
self._get_file_url, await file_code.get_file_path(), filename
619645
)
620646

647+
content_length = file_code.size # 默认使用数据库中的大小
648+
649+
# 尝试发送HEAD请求获取Content-Length
650+
try:
651+
async with aiohttp.ClientSession() as session:
652+
async with session.head(link) as resp:
653+
if resp.status == 200 and 'Content-Length' in resp.headers:
654+
content_length = int(resp.headers['Content-Length'])
655+
except Exception:
656+
# 如果HEAD请求失败,继续使用默认大小
657+
pass
658+
621659
async def stream_generator():
622660
async with aiohttp.ClientSession() as session:
623661
async with session.get(link) as resp:
@@ -635,7 +673,7 @@ async def stream_generator():
635673

636674
headers = {
637675
"Content-Disposition": f'attachment; filename="{filename.encode("utf-8").decode("latin-1")}"',
638-
"Content-Length": str(file_code.size)
676+
"Content-Length": str(content_length)
639677
}
640678
return StreamingResponse(
641679
stream_generator(),
@@ -804,6 +842,19 @@ async def get_file_url(self, file_code: FileCodes):
804842
async def get_file_response(self, file_code: FileCodes):
805843
try:
806844
filename = file_code.prefix + file_code.suffix
845+
content_length = file_code.size # 默认使用数据库中的大小
846+
847+
# 尝试获取文件大小
848+
try:
849+
stat_result = await self.operator.stat(await file_code.get_file_path())
850+
if hasattr(stat_result, 'content_length') and stat_result.content_length:
851+
content_length = stat_result.content_length
852+
elif hasattr(stat_result, 'size') and stat_result.size:
853+
content_length = stat_result.size
854+
except Exception:
855+
# 如果获取大小失败,继续使用默认大小
856+
pass
857+
807858
# 尝试使用流式读取器
808859
try:
809860
# OpenDAL 可能提供 reader 方法返回一个异步读取器
@@ -813,7 +864,7 @@ async def get_file_response(self, file_code: FileCodes):
813864
content = await self.operator.read(await file_code.get_file_path())
814865
headers = {
815866
"Content-Disposition": f'attachment; filename="{filename}"',
816-
"Content-Length": str(file_code.size)
867+
"Content-Length": str(content_length)
817868
}
818869
return Response(
819870
content, headers=headers, media_type="application/octet-stream"
@@ -829,7 +880,7 @@ async def stream_generator():
829880

830881
headers = {
831882
"Content-Disposition": f'attachment; filename="{filename}"',
832-
"Content-Length": str(file_code.size)
883+
"Content-Length": str(content_length)
833884
}
834885
return StreamingResponse(
835886
stream_generator(),
@@ -1023,6 +1074,19 @@ async def get_file_response(self, file_code: FileCodes):
10231074
try:
10241075
filename = file_code.prefix + file_code.suffix
10251076
url = self._build_url(await file_code.get_file_path())
1077+
content_length = file_code.size # 默认使用数据库中的大小
1078+
1079+
# 尝试发送HEAD请求获取Content-Length
1080+
try:
1081+
async with aiohttp.ClientSession(headers={
1082+
"Authorization": f"Basic {base64.b64encode(f'{settings.webdav_username}:{settings.webdav_password}'.encode()).decode()}"
1083+
}) as session:
1084+
async with session.head(url) as resp:
1085+
if resp.status == 200 and 'Content-Length' in resp.headers:
1086+
content_length = int(resp.headers['Content-Length'])
1087+
except Exception:
1088+
# 如果HEAD请求失败,继续使用默认大小
1089+
pass
10261090

10271091
async def stream_generator():
10281092
async with aiohttp.ClientSession(headers={
@@ -1043,7 +1107,7 @@ async def stream_generator():
10431107

10441108
headers = {
10451109
"Content-Disposition": f'attachment; filename="{filename.encode("utf-8").decode()}"',
1046-
"Content-Length": str(file_code.size)
1110+
"Content-Length": str(content_length)
10471111
}
10481112
return StreamingResponse(
10491113
stream_generator(),

0 commit comments

Comments
 (0)