让Django提供可下载文件
我希望网站上的用户能够下载一些文件,但这些文件的路径要隐藏起来,这样他们就不能直接下载了。
比如,我希望链接看起来像这样:http://example.com/download/?f=somefile.txt
而在服务器上,我知道所有可以下载的文件都在这个文件夹里:/home/user/files/
.
有没有办法让Django直接提供这个文件下载,而不是去找一个链接和视图来显示它呢?
15 个回答
对于一个非常简单但不高效也不适合大规模使用的解决方案,你可以直接使用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))
“下载”其实就是改变一个HTTP头部的信息。
想了解如何让浏览器把响应当作文件下载,可以查看这个链接:http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment。
你只需要定义一个URL,比如"/download"
。
请求中的GET
或POST
字典会包含"f=somefile.txt"
的信息。
你的视图函数只需要把基本路径和"f"
的值合并,打开文件,然后创建并返回一个响应对象。代码应该不超过12行。
如果你想要“兼得其利”,可以把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头。