264

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

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

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

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

4

15 回答 15

195

为了“两全其美”,您可以将 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 时才有效。

编辑:

mimetype 被 django 1.7 的 content_type 取代

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

编辑: 为了nginx检查这个,它使用X-Accel-Redirect而不是apacheX-Sendfile 标头。

于 2009-07-21T11:57:59.287 回答
87

“下载”只是 HTTP 标头更改。

有关如何响应下载,请参阅http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment .

您只需为"/download".

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

您的视图函数将简单地将基本路径与“ f”值合并,打开文件,创建并返回一个响应对象。它应该少于 12 行代码。

于 2009-07-20T23:20:15.970 回答
35

对于一个非常简单但不高效或可扩展的解决方案,您可以只使用内置的 djangoserve视图。这对于快速原型或一次性工作非常有用,但正如在整个问题中提到的那样,您应该在生产中使用 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))
于 2014-01-18T22:54:28.477 回答
27

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

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

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

同样,S.Lott 的 X-SendFile 仍然更适合较大的文件。但是,如果您不能或不想为此烦恼,那么这个中间解决方案将为您带来更高的效率,而无需麻烦。

于 2010-12-30T19:17:20.437 回答
17

尝试了@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

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

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

于 2013-09-24T15:22:06.223 回答
17

只是提到了Django 1.10 中可用的FileResponse对象

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

视图.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

网址.py

...
url(r'^somefile/(?P<filename>[-\w_\\-\\.]+)$', views.SomeFileDownloadView.as_view(), name='somefile-download'),
...
于 2017-03-20T07:51:34.020 回答
13

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

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

X-SendFile-Encoding: url

也送了。

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

于 2011-03-14T17:10:18.967 回答
7

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

“一旦 Django 检查了权限等,就将文件上传卸载到 Web 服务器(例如带有 mod_xsendfile 的 Apache)的抽象。”

于 2011-04-14T06:37:37.400 回答
6

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

用法

pip install django-fileprovider
  • 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  

django-fileprovider以您的代码只需要最少修改的方式实施。

Nginx 配置

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

 location /files/ {
  internal;
  root   /home/sideffect0/secret_files/;
 }

这里nginx设置了一个仅在内部访问的位置 url /files/,如果您使用上述配置,您可以将 X-File 设置为,

response['X-File'] = '/files/filename.extension' 

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

于 2016-12-12T18:35:40.590 回答
3
def qrcodesave(request): 
    import urllib2;   
    url ="http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=s&chld=H|0"; 
    opener = urllib2.urlopen(url);  
    content_type = "application/octet-stream"
    response = HttpResponse(opener.read(), content_type=content_type)
    response["Content-Disposition"]= "attachment; filename=aktel.png"
    return response 
于 2013-12-16T11:26:06.340 回答
2

Django 建议您使用另一台服务器来提供静态媒体(在同一台机器上运行的另一台服务器很好。)他们建议使用诸如lighttp之类的服务器。

这很容易设置。然而。如果“somefile.txt”是根据请求生成的(内容是动态的),那么您可能希望 django 提供它。

Django Docs - 静态文件

于 2009-07-21T09:49:56.423 回答
0

另一个要看的项目:http: //readthedocs.org/docs/django-private-files/en/latest/usage.html 看起来很有希望,但我自己还没有测试过。

基本上,该项目抽象了 mod_xsendfile 配置并允许您执行以下操作:

from django.db import models
from django.contrib.auth.models import User
from private_files import PrivateFileField

def is_owner(request, instance):
    return (not request.user.is_anonymous()) and request.user.is_authenticated and
                   instance.owner.pk = request.user.pk

class FileSubmission(models.Model):
    description = models.CharField("description", max_length = 200)
        owner = models.ForeignKey(User)
    uploaded_file = PrivateFileField("file", upload_to = 'uploads', condition = is_owner)
于 2012-01-13T06:18:31.057 回答
0

我不止一次遇到同样的问题,所以使用 xsendfile 模块和 auth view decorators django-filelibrary 实现。随意将其用作您自己解决方案的灵感。

https://github.com/danielsokolowski/django-filelibrary

于 2012-05-01T22:07:51.167 回答
0

使用https://github.com/johnsensible/django-sendfile提供对静态 html 文件夹的受保护访问:https ://gist.github.com/iutinvg/9907731

于 2014-04-01T04:35:39.063 回答
0

我为此做了一个项目。你可以看看我的 github 仓库:

https://github.com/nishant-boro/django-rest-framework-download-expert

该模块提供了一种使用 Apache 模块 Xsendfile 在 django rest 框架中提供文件以供下载的简单方法。它还具有仅向属于特定组的用户提供下载的附加功能

于 2019-08-11T10:58:02.500 回答