http://example.com/download/?f=somefile.txt
在服务器上,我知道所有可下载文件都位于文件夹
/home/user/files/
中。是否有一种方法可以使Django提供该文件供下载,而不是尝试查找URL。和查看以显示它?
#1 楼
对于“两全其美”,您可以将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已由content_type替换为django 1.7
response = HttpResponse(content_type='application/force-download')
编辑:
对于
nginx
进行检查,它使用X-Accel-Redirect
而不是apache
X-Sendfile标头。评论
如果您的文件名或path_to_file包含非ASCII字符,例如“ä”或“ö”,则smart_str无法正常工作,因为apache模块X-Sendfile无法解码smart_str编码的字符串。因此,例如“Örinää.mp3”文件无法提供。如果忽略了smart_str,则Django本身会引发ascii编码错误,因为在发送之前,所有标头均已编码为ascii格式。我知道解决此问题的唯一方法是将X-sendfile文件名减少为仅包含ascii的文件名。
–中国
2010年5月31日16:13
更清楚地说:S.Lott有一个简单的示例,仅直接从Django提供文件,不需要其他设置。 elo80ka有一个更有效的示例,其中Web服务器处理静态文件,而django则不必。后者具有更好的性能,但可能需要更多的设置。两者都有自己的位置。
–rocketmonkeys
2010-12-30 19:12
@Ciantic,请参阅btimby的答案,以了解编码问题的解决方案。
– mlissner
2012-2-14在7:36
此解决方案是否可与以下Web服务器配置一起使用?后端:2台或更多台Apache + mod_wsgi个体(VPS)服务器设置为可以相互复制。前端:1台使用上游负载均衡的nginx代理(VPS)服务器,进行轮询。
–丹尼尔(Daniel)
15年7月6日在15:13
django 1.7将mimetype替换为content_type
–ismailsunni
15年8月4日在4:04
#2 楼
“下载”只是HTTP标头更改。请参见http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat响应作为文件附件,以了解如何进行下载。
"/download"
只需要一个URL定义。请求的
GET
或POST
字典将包含"f=somefile.txt"
信息。您的视图函数将简单地将基本路径与“
f
”值合并,打开文件,创建并返回响应对象。它应该少于12行代码。评论
这本质上是正确的(简单的)答案,但是要小心-将文件名作为参数传递意味着用户可以下载任何文件(即,如果传递“ f = / etc / passwd”该怎么办?)可以防止这种情况发生的事情(用户权限等),但请注意这种明显但常见的安全风险。它基本上只是验证输入的一个子集:如果将文件名传递给视图,请在该视图中检查文件名!
–rocketmonkeys
2010-12-30 19:10
解决此安全问题的一个非常简单的方法:filepath = filepath.replace('..','').replace('/','')
– duality_
13年7月23日在20:24
如果您使用表来存储文件信息(包括哪些用户应该能够下载文件),则您需要发送的只是主键而不是文件名,然后由应用程序决定要执行的操作。
–爱德华·纽厄尔
2015年5月5日下午4:25
#3 楼
对于一个非常简单但效率不高或可扩展的解决方案,您可以仅使用内置的dqqq1202079q视图。这非常适合快速原型或一次性工作,但正如在整个问题中已经提到的那样,在生产中应该使用apache或nginx之类的东西。评论
对于在Windows上提供测试的后备功能也非常有用。
–阿米尔·阿里·阿克巴里(Amir Ali Akbari)
2014年1月21日,9:58
为什么效率不高?
–缩放
2014年3月8日在16:02
@zinking因为文件通常应该通过apache之类的文件而不是通过django进程提供
– Cory
2014年8月8日在21:04
我们在这里谈论什么样的性能缺陷?文件是否通过django加载到RAM或某种形式的文件中?为什么django无法提供与nginx相同的效率?
– Gershom
15年7月6日在15:05
@GershomMaes不是真正的解释,但是官方文档说它效率极低并且可能不安全,我想他们知道他们在谈论docs.djangoproject.com/en/1.8/howto/static-files
–马克
15年10月25日在9:58
#4 楼
S.Lott具有“良好” /简单的解决方案,而elo80ka具有“最佳” /高效的解决方案。这是一个“更好的” /中间解决方案-无需服务器设置,但对于大型文件而言,比朴素的修补程序更有效:http://djangosnippets.org/snippets/365/
基本上,Django仍会处理文件的提供,但不会立即将整个文件加载到内存中。这样一来,您的服务器就可以(缓慢地)为大文件提供服务而又不会增加内存使用量。
同样,S.Lott的X-SendFile对于较大的文件仍然更好。但是,如果您不能或不想为此烦恼,那么此中间解决方案将为您带来更高的效率,而无需麻烦。
评论
那段代码不好。该片段依赖于django.core.servers.httpbase未记录的私有模块,该模块在代码“请勿用于生产用途!”的顶部有一个很大的警告标志,该文件自首先创建。无论如何,此代码段依赖的FileWrapper功能已在django 1.9中删除。
– eykanal
2015年12月4日15:18
#5 楼
刚刚提到了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
urls.py
...
url(r'^somefile/(?P<filename>[-\w_\-\.]+)$', views.SomeFileDownloadView.as_view(), name='somefile-download'),
...
评论
非常感谢你!它是下载二进制文件的最简单的解决方案,并且有效。
–赵茱莉亚
17-4-16在23:01
#6 楼
尝试过@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的答案,得到了将所有这些都结合在一起的完美解决方案=)
评论
谢谢,这正是我想要的!
– ihatecache
2015年9月2日19:23
在Content-Disposition标头中的文件名filename =“%s”周围添加双引号,以避免文件名中的空格出现问题。参考:下载时,带有空格的文件名将被截断,如何在HTTP中编码Content-Disposition标头的filename参数?
–克里斯蒂安·朗(Christian Long)
16年1月4日在18:12
您的解决方案对我有用。但是我的文件出现“无效的起始字节...”错误。用FileWrapper(open(path.abspath(file_name),'rb'))解决
– Mark Mishyn
17年1月26日在10:41
自Django 1.9起已删除FileWrapper
– freethebees
17年6月21日在15:56
可以从wsgiref.util中使用FileWrapper导入
–克里斯
17年8月4日在11:19
#7 楼
上面提到过,mod_xsendfile方法不允许文件名中包含非ASCII字符。为此,我有一个可用于mod_xsendfile的补丁程序,只要该文件可以发送即可。名称是url编码的,还有附加的标头:
X-SendFile-Encoding: url
也发送了。http://ben.timby.com /?p = 149
评论
现在,补丁已折叠到corer库中。
– mlissner
2012-2-14在7:33
#8 楼
尝试:https://pypi.python.org/pypi/django-sendfile/“一旦Django检查了权限等,就可以将文件上传到Web服务器(例如,带有mod_xsendfile的Apache)的抽象卸载。”
评论
当时(一年前),我的个人叉子有非Apache文件,可用于回退原始存储库尚未包含的文件。
–罗伯托·罗萨里奥(Roberto Rosario)
2012年5月15日19:38
为什么删除链接?
–kiok46
15年11月12日在20:38
@ kiok46与Github政策冲突。编辑以指向规范地址。
–罗伯托·罗萨里奥(Roberto Rosario)
16年1月22日在8:46
#9 楼
您应在生产环境中使用常见服务器(如apache
或nginx
)提供的sendfile api。多年以来,我一直在使用这些服务器的sendfile api保护文件。然后为此目的创建了一个简单的基于django的简单中间件应用程序,既适合开发,也适合生产。您可以在此处访问源代码。
更新:在新版本中,
python
提供程序使用django FileResponse
(如果可用),并且还增加了对许多服务器的支持从lighthttp,caddy到hiawatha的实现用法
pip install django-fileprovider
将
fileprovider
应用添加到INSTALLED_APPS
设置,将
fileprovider.middleware.FileProviderMiddleware
添加到MIDDLEWARE_CLASSES
设置将
FILEPROVIDER_NAME
设置添加到nginx
或apache
(在生产环境中),默认情况下是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
中控制文件#10 楼
Django建议您使用另一台服务器来提供静态媒体(在同一台计算机上运行的另一台服务器就可以了。)他们建议使用诸如lighttp之类的服务器。设置非常简单。然而。如果“ somefile.txt”是根据请求生成的(内容是动态的),则您可能希望django提供它。
Django Docs-静态文件
#11 楼
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
#12 楼
另一个要查看的项目:http://readthedocs.org/docs/django-private-files/zh-CN/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)
评论
request.user.is_authenticated是方法,而不是属性。 (不是request.user.is_anonymous())与request.user.is_authenticated()完全相同,因为is_authenticated与is_anonymous相反。
–爆炸
2012年8月6日15:07
@explodes更糟糕的是,该代码是正确的django-private-files文档中的代码...
– ArmandoPérezMarqués
13年2月25日在20:39
#13 楼
我再一次遇到相同的问题,因此使用django-filelibrary的xsendfile模块和auth视图修饰符实现了该问题。随意将其用作您自己的解决方案的灵感。https://github.com/danielsokolowski/django-filelibrary
#14 楼
使用https://github.com/johnsensible/django-sendfile提供对静态html文件夹的受保护访问:https://gist.github.com/iutinvg/9907731#15 楼
我为此做了一个项目。您可以看一下我的github存储库:https://github.com/nishant-boro/django-rest-framework-download-expert
此模块提供了一个简单的方法Apache模块Xsendfile在django rest框架中提供下载文件的方法。它还具有一个附加功能,即仅向属于特定组的用户提供下载
评论
您为什么不简单地使用Apache来做到这一点? Apache提供的静态内容比Django更快,更简单。我不使用Apache,因为我不希望这些文件没有基于Django的权限就可以访问。
如果要考虑用户权限,则必须通过Django的视图提供文件
正是,这就是为什么我问这个问题。