如何在Django中实现Windows服务器共享文件的下载功能?
问题:Django应用中实现Windows共享文件的正常下载链接
背景
- 开发的Django应用展示工单查询结果,包含指向Windows服务器共享目录的文件下载链接,路径格式为
\\server.name\folder A\folder B\folder C\待下载文件.txt - 固定路径部分:
\\server.name\folder A\folder B - 动态内容:
\folder C及待下载文件名 - 所有文件已存储在Windows共享目录,无需用户上传
当前问题
模板页面中表格内的下载链接,鼠标悬停时浏览器显示路径为file://server.name/folder A/folder B\\folder C\\file to download.txt,斜杠格式混乱;传递到模板的原始路径是\\server.name\\folder A\\folder B\\folder C\\file to download.txt,点击链接无法正常下载文件。
相关代码
模板文件(ticket.html)
<body> <h1><p>Ticket Number: {{ thisincident.IncidentID }}</p></h1> <h3>Description: {{ thisincident.Description }}</h3> <h4><p>Full Description</h4> {{ thisincident.FullDescription | linebreaks }}</p> <table> <thead> <tr> <th>Journal Entry #</th> <th>Date</th> <th>Person</th> <th>Journal Entry</th> <th>File Name</th> </tr> </thead> <tbody> {% for f in je %} <tr> <td>{{ f.JE }}</td> <td>{{ f.Date }}</td> <td>{{ f.resource }}</td> <td>{{ f.entry | linebreaks }}</td> <td><a href="{{ f.Location }}">Download</a></td> </tr> {% endfor %} </tbody> </table> </body>
views.py
def ticket_view(request, IncidentID): file_contents = [] thisincident = ticket.objects.get(id=IncidentID) for root, dirs, files in os.walk(thisincident.LocationPath): for file in files: if not file == 'manifest.json': path_file = os.path.join(root, file) path_file = path_file.replace(thisincident.LocationPath + "\\", "") file_contents.append(path_file) log_list = parseJElog(thisincident.logfiletoparse) data = { "thisincident": thisincident, "result": file_contents, "je":log_list } return render(request, "ticket.html", data)
urls.py
urlpatterns = [ url(r'^homepage/$', views.home, name='home'), url(r'^tickets/$', views.tickets, name='tickets'), url(r'^ticket/(?P<IncidentID>\d+)$', views.ticket_view, name='ticket'), ] if settings.DEBUG: urlpatterns+=static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
settings.py
MEDIA_BASE_ROOT = "\\server.name\\folder A\\folder B" MEDIA_ROOT = os.path.join(MEDIA_BASE_ROOT, "\\folder B\\") MEDIA_URL = ''
解决方案
核心问题分析
直接使用file://协议访问Windows共享文件会触发浏览器安全策略拦截,同时存在跨域限制和路径格式混乱问题。正确做法是通过Django后端作为代理,读取共享文件后返回给用户,而非直接暴露共享路径。
步骤1:新增下载处理视图
在views.py中添加专门处理文件下载的视图,统一路径格式并做安全校验:
import os from django.http import FileResponse, Http404 from django.conf import settings def download_shared_file(request, IncidentID, file_path): # 拼接完整共享路径,统一格式 full_path = os.path.normpath(os.path.join(settings.MEDIA_BASE_ROOT, file_path)) # 校验文件合法性,防止目录遍历攻击 if not os.path.exists(full_path) or not os.path.isfile(full_path): raise Http404("文件不存在") # 返回文件下载响应 response = FileResponse(open(full_path, 'rb')) response['Content-Disposition'] = f'attachment; filename="{os.path.basename(full_path)}"' return response
步骤2:配置下载路由
在urls.py中添加下载路由,支持动态文件路径:
urlpatterns = [ url(r'^homepage/$', views.home, name='home'), url(r'^tickets/$', views.tickets, name='tickets'), url(r'^ticket/(?P<IncidentID>\d+)$', views.ticket_view, name='ticket'), # 新增下载路由,匹配任意子路径 url(r'^ticket/(?P<IncidentID>\d+)/download/(?P<file_path>.*)$', views.download_shared_file, name='download_shared_file'), ]
步骤3:修改模板链接
将模板中的直接路径替换为Django反向解析的URL:
<td><a href="{% url 'download_shared_file' IncidentID=thisincident.IncidentID file_path=f.Location %}">Download</a></td>
步骤4:优化路径处理(可选)
在ticket_view中确保传递给模板的是相对正斜杠路径,避免转义问题:
# 替换原路径处理逻辑 path_file = os.path.relpath(os.path.join(root, file), settings.MEDIA_BASE_ROOT) path_file = path_file.replace(os.sep, '/') file_contents.append(path_file)
关键注意事项
- 权限配置:确保Django运行账户拥有Windows共享目录的访问权限(需配置共享权限和NTFS权限)
- 安全防护:通过
os.path.normpath和存在性检查,阻断目录遍历攻击 - 跨平台兼容:统一使用正斜杠路径,避免Windows与Web路径格式冲突
内容的提问来源于stack exchange,提问作者Gary Swartz
相关产品推荐
相关产品推荐

