让Django提供可下载的文件

dam*_*mon 231 python django download

我希望网站上的用户能够下载路径被遮挡的文件,因此无法直接下载.

例如,我希望URL是这样的," http://example.com/download/?f=somefile.txt

在服务器上,我知道所有可下载文件都位于"/ home/user/files /"文件夹中.

有没有办法让Django提供该文件供下载,而不是试图找到一个URL和View来显示它?

elo*_*0ka 185

对于"两全其美",您可以将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
Run Code Online (Sandbox Code Playgroud)

当然,这只有在您控制服务器或托管公司已设置mod_xsendfile时才有效.

编辑:

对于django 1.7,mimetype被content_type替换

response = HttpResponse(content_type='application/force-download'  
Run Code Online (Sandbox Code Playgroud)

编辑: 为了nginx检查这一点,它使用X-Accel-Redirect而不是apacheX-Sendfile头.

  • 对于django 1.7,mimetype被content_type替换 (12认同)
  • 如果您的文件名或path_to_file包含非ascii字符,例如"ä"或"ö",则"smart_str"无法按预期工作,因为apache模块X-Sendfile无法解码smart_str编码的字符串.因此,例如"Örinää.mp3"文件无法提供.如果省略了smart_str,Django本身会抛出ascii编码错误,因为所有*headers*在发送之前都被编码为ascii格式.我知道绕过这个问题的唯一方法是将X-sendfile文件名减少为仅包含ascii的文件名. (6认同)
  • 更清楚:S.Lott有一个简单的例子,只是直接从django提供文件,不需要其他设置.elo80ka有更有效的例子,其中web服务器处理静态文件而django不需要.后者具有更好的性能,但可能需要更多设置.两者都有自己的位置. (3认同)

S.L*_*ott 85

"下载"只是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行代码.

  • 这本质上是正确的(简单)答案,但是一个注意事项 - 将文件名作为参数传递意味着用户可以下载*any*文件(即,如果你传递"f =/etc/passwd",那该怎么办?)很多事情都有助于防止这种情况(用户权限等),但只要注意这种明显但常见的安全风险.它基本上只是验证输入的一个子集:如果您将文件名传递给视图,请检查该视图中的文件名! (47认同)
  • 这个安全问题的一个_very simple_修复:`filepath = filepath.replace('..','').replace('/','')` (8认同)
  • 如果您使用表来存储文件信息,包括哪些用户应该能够下载它,那么您需要发送的只是主键,而不是文件名,应用程序决定该怎么做. (7认同)

roc*_*eys 27

S.Lott拥有"好"/简单的解决方案,而elo80ka拥有"最佳"/高效的解决方案.这是一个"更好"/中间的解决方案 - 没有服务器设置,但对于大型文件比天真的修复更有效:

http://djangosnippets.org/snippets/365/

基本上,Django仍然处理文件服务,但不会立即将整个内容加载到内存中.这允许您的服务器(缓慢地)提供大文件而不会增加内存使用量.

同样,S.Lott的X-SendFile对于较大的文件仍然更好.但是如果你不能或不想打扰它,那么这个中间解决方案可以让你获得更高的效率而不会有麻烦.

  • 那个片段不好.这文档片断依靠`django.core.servers.httpbase`无证私人模块,其具有的代码"的顶部大警告标志上[请勿用于生产使用!!!(https://开头github上. COM/django的/ django的/斑点/主/ django的/核心/服务器/ basehttp.py)",这已在文件中[因为它是第一次创建(https://github.com/django/django/blob/ b68c478aa5d890e76aae6e2f695220505618c8e0/django的/核心/服务器/ basehttp.py).无论如何,这个代码片段依赖的`FileWrapper`功能已在django 1.9中删除. (4认同)

Cor*_*ory 27

对于非常简单但不高效或可扩展的解决方案,您可以使用内置的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))
Run Code Online (Sandbox Code Playgroud)

  • 为什么它效率不高? (2认同)
  • @zinking 因为文件通常应该通过 apache 之类的东西而不是通过 django 进程提供 (2认同)

Sal*_*lab 16

试过@Rocketmonkeys解决方案,但下载的文件存储为*.bin并给出随机名称.那当然不好.从@ elo80ka添加另一行解决了这个问题.
这是我现在使用的代码:

from wsgiref.util import FileWrapper
from django.http import HttpResponse

filename = "/home/stackoverflow-addict/private-folder(not-porn)/image.jpg"
wrapper = FileWrapper(file(filename))
response = HttpResponse(wrapper, content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(filename)
response['Content-Length'] = os.path.getsize(filename)
return response
Run Code Online (Sandbox Code Playgroud)

您现在可以将文件存储在私人目录中(不在/ media或/ public_html内),并通过django将它们暴露给某些用户或在某些情况下.
希望能帮助到你.

感谢@ elo80ka,@ S.Lott和@Rocketmonkeys的答案,得到了完美的解决方案,结合了所有这些=)


sha*_*adi 15

只需提及Django 1.10中提供的FileResponse对象

编辑:在搜索通过Django传输文件的简单方法时遇到了我自己的答案,所以这里有一个更完整的例子(对于未来我).它假定FileField名称是imported_file

views.py

from django.views.generic.detail import DetailView   
from django.http import FileResponse
class BaseFileDownloadView(DetailView):
  def get(self, request, *args, **kwargs):
    filename=self.kwargs.get('filename', None)
    if filename is None:
      raise ValueError("Found empty filename")
    some_file = self.model.objects.get(imported_file=filename)
    response = FileResponse(some_file.imported_file, content_type="text/csv")
    # https://docs.djangoproject.com/en/1.11/howto/outputting-csv/#streaming-large-csv-files
    response['Content-Disposition'] = 'attachment; filename="%s"'%filename
    return response

class SomeFileDownloadView(BaseFileDownloadView):
    model = SomeModel
Run Code Online (Sandbox Code Playgroud)

urls.py

...
url(r'^somefile/(?P<filename>[-\w_\\-\\.]+)$', views.SomeFileDownloadView.as_view(), name='somefile-download'),
...
Run Code Online (Sandbox Code Playgroud)


bti*_*mby 13

上面提到mod_xsendfile方法不允许文件名中包含非ASCII字符.

出于这个原因,我有一个可用于mod_xsendfile的补丁,它允许发送任何文件,只要名称是url编码的,以及附加标题:

X-SendFile-Encoding: url
Run Code Online (Sandbox Code Playgroud)

也被送了.

http://ben.timby.com/?p=149


Rob*_*rio 7

尝试:https://pypi.python.org/pypi/django-sendfile/

"一旦Django检查了权限等,就抽象将文件上传到web服务器(例如带有mod_xsendfile的Apache)"

  • 当时(1年前),我的私人分支有非Apache文件服务回退,原始存储库尚未包括在内. (2认同)

Ren*_*han 6

你应该使用像流行服务器一样apachenginx 在生产中给出的sendfile apis .多年来我一直在使用这些服务器的sendfile api来保护文件.然后为此目的创建了一个简单的基于中间件的django应用程序,适用于开发和生产目的.您可以在此处访问源代码.
更新:在新版本python提供程序中使用django(FileResponse如果可用)并且还添加了对lighthttp,caddy到hiawatha的许多服务器实现的支持

用法

pip install django-fileprovider
Run Code Online (Sandbox Code Playgroud)
  • fileprovider应用添加到INSTALLED_APPS设置中
  • 添加fileprovider.middleware.FileProviderMiddlewareMIDDLEWARE_CLASSES设置
  • FILEPROVIDER_NAME设置设置为生产nginxapache生产,默认情况下python用于开发目的.

在基于类或函数的视图中,将响应标头X-File值设置为文件的绝对路径.例如,

def hello(request):  
   // code to check or protect the file from unauthorized access
   response = HttpResponse()  
   response['X-File'] = '/absolute/path/to/file'  
   return response  
Run Code Online (Sandbox Code Playgroud)

django-fileprovider 以某种方式强调您的代码只需要最少的修改.

Nginx配置

要保护文件不被直接访问,您可以将配置设置为

 location /files/ {
  internal;
  root   /home/sideffect0/secret_files/;
 }
Run Code Online (Sandbox Code Playgroud)

这里nginx设置一个位置url /files/只访问internaly,如果你使用上面的配置,你可以设置X-File为,

response['X-File'] = '/files/filename.extension' 
Run Code Online (Sandbox Code Playgroud)

通过使用nginx配置执行此操作,该文件将受到保护,您还可以从django控制该文件 views