Django项目实现客户端从服务端下载pdf、docx等文件的问题求解
Django 服务端文件下载功能实现方案
问题根源
webbrowser.open() 是运行在Django服务端进程中的Python接口,仅能操作服务器本地的浏览器资源,完全无法触达访问应用的客户端用户设备,因此不能用于实现客户端下载功能,必须通过Django的HTTP响应机制实现。
标准实现步骤
1. 编写下载视图函数
适用于100M以下的小文件场景,直接返回FileResponse即可:
import os from django.conf import settings from django.http import FileResponse, Http404 def download_file(request, filename): # 拼接服务器上的文件存储路径,建议将公开下载文件放在MEDIA_ROOT下的指定目录 file_base_dir = os.path.join(settings.MEDIA_ROOT, 'internal_documents') file_path = os.path.abspath(os.path.join(file_base_dir, filename)) # 路径校验,防止路径遍历攻击 if not file_path.startswith(file_base_dir) or not os.path.exists(file_path): raise Http404("请求的文件不存在") # 构造下载响应 response = FileResponse(open(file_path, 'rb')) # 设置响应类型为二进制流,触发下载而非浏览器直接打开 response['Content-Type'] = 'application/octet-stream' # 处理中文文件名乱码问题 encoded_filename = filename.encode('utf-8').decode('latin1') response['Content-Disposition'] = f'attachment;filename="{encoded_filename}"' return response
2. 绑定路由
在应用的urls.py中添加下载路由:
from django.urls import path from . import views urlpatterns = [ # 其他现有路由 path('internal/download/<str:filename>/', views.download_file, name='internal_download'), ]
3. 前端调用
在页面中直接通过a标签指向下载路由即可,用户点击后会自动触发客户端本地的下载弹窗:
<a href="{% url 'internal_download' filename='员工手册.pdf' %}">下载员工手册</a> <a href="{% url 'internal_download' filename='项目规范.docx' %}">下载项目规范</a>
大文件优化方案
如果需要下载超过100M的大文件,建议使用StreamingHttpResponse分片传输,避免占用服务端过多内存:
from django.http import StreamingHttpResponse def download_large_file(request, filename): file_base_dir = os.path.join(settings.MEDIA_ROOT, 'internal_documents') file_path = os.path.abspath(os.path.join(file_base_dir, filename)) if not file_path.startswith(file_base_dir) or not os.path.exists(file_path): raise Http404("请求的文件不存在") # 分片读取文件迭代器 def file_iterator(chunk_size=8192): with open(file_path, 'rb') as f: while chunk := f.read(chunk_size): yield chunk response = StreamingHttpResponse(file_iterator()) response['Content-Type'] = 'application/octet-stream' encoded_filename = filename.encode('utf-8').decode('latin1') response['Content-Disposition'] = f'attachment;filename="{encoded_filename}"' return response
注意事项
- 若需要限制仅内部员工访问,可直接在视图函数中添加登录校验、权限校验逻辑即可
- 不要省略路径校验步骤,避免攻击者构造
../类的特殊路径下载服务器上的敏感文件 - 若文件存储在对象存储等非本地路径,可修改读取逻辑直接从对象存储接口获取文件流返回即可,无需落地到服务器本地磁盘
内容的提问来源于stack exchange,提问作者Jfka
相关产品推荐
相关产品推荐

