让Django提供可下载文件

274 投票
15 回答
193376 浏览
提问于 2025-04-15 13:02

我希望网站上的用户能够下载一些文件,但这些文件的路径要隐藏起来,这样他们就不能直接下载了。

比如,我希望链接看起来像这样:http://example.com/download/?f=somefile.txt

而在服务器上,我知道所有可以下载的文件都在这个文件夹里:/home/user/files/.

有没有办法让Django直接提供这个文件下载,而不是去找一个链接和视图来显示它呢?

15 个回答

37

对于一个非常简单但不高效也不适合大规模使用的解决方案,你可以直接使用Django自带的serve视图。这种方法非常适合快速制作原型或临时工作,但正如这个问题中提到的,你在正式环境中应该使用像Apache或Nginx这样的服务器。

from django.views.static import serve
filepath = '/some/path/to/local/file.txt'
return serve(request, os.path.basename(filepath), os.path.dirname(filepath))
89

“下载”其实就是改变一个HTTP头部的信息。

想了解如何让浏览器把响应当作文件下载,可以查看这个链接:http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment

你只需要定义一个URL,比如"/download"

请求中的GETPOST字典会包含"f=somefile.txt"的信息。

你的视图函数只需要把基本路径和"f"的值合并,打开文件,然后创建并返回一个响应对象。代码应该不超过12行。

202

如果你想要“兼得其利”,可以把S.Lott的解决方案和xsendfile模块结合起来使用:Django会生成文件的路径(或者直接生成文件),但实际的文件传输是由Apache或Lighttpd来处理的。一旦你设置好mod_xsendfile,和你的视图结合起来只需要几行代码:

from django.utils.encoding import smart_str

response = HttpResponse(mimetype='application/force-download') # mimetype is replaced by content_type for django 1.7
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
# It's usually a good idea to set the 'Content-Length' header too.
# You can also set any other required headers: Cache-Control, etc.
return response

当然,这个方法只有在你能控制自己的服务器,或者你的托管公司已经设置好mod_xsendfile的情况下才能使用。

编辑:

在Django 1.7中,mimetype被content_type替代了。

response = HttpResponse(content_type='application/force-download')  

编辑: 如果你使用的是nginx,可以查看这个链接,它使用的是X-Accel-Redirect,而不是apache的X-Sendfile头。

撰写回答