在 Django 中提供大文件(高负载)

Posted

技术标签:

【中文标题】在 Django 中提供大文件(高负载)【英文标题】:Serving large files ( with high loads ) in Django 【发布时间】:2012-01-25 21:58:27 【问题描述】:

我一直在使用一种提供下载的方法,但由于它不安全,我决定改变它。 (该方法是一个指向存储中原始文件的链接,但风险是每个知道该链接的人都可以下载该文件!)所以我现在通过我的视图提供该文件,这样只有具有权限的用户才能下载该文件,但我注意到服务器上的负载很高,同时有许多文件同时下载请求。这是我处理用户下载的代码的一部分(考虑文件是图像)

    image = Image.open ("the path to file")
    response = HttpResponse(mimetype = 'image/png' )
    response['Content-Disposition'] = 'attachment: filename=%s.png' % filename
    image.save(response , "png")
    return response  

有没有更好的方法来提供文件,同时保持安全性和降低服务器端负载? 在此先感谢:)

【问题讨论】:

你为什么打开图片,只是为了再次保存? @burhan 我已经打开了文件,所以我可以访问它并将其作为 png 图像文件提供,可以在不打开图像的情况下完成吗? 直到所有酷孩子都停止使用 mod_python 之前,您可以让 apache 从 Django 的身份验证系统进行身份验证:docs.djangoproject.com/en/dev/howto/apache-auth 但现在所有酷孩子都使用 WSGI(和 nginx)。基于这些的解决方案将对更广泛的社区有用, 【参考方案1】:

您打开图像会将其加载到内存中,这就是导致大量使用时负载增加的原因。正如 Martin 所发布的,真正的解决方案是直接提供文件。

这是另一种方法,它将文件分块流式传输,而不将其加载到内存中。

import os
import mimetypes
from django.http import StreamingHttpResponse
from django.core.servers.basehttp import FileWrapper


def download_file(request):
   the_file = '/some/file/name.png'
   filename = os.path.basename(the_file)
   chunk_size = 8192
   response = StreamingHttpResponse(FileWrapper(open(the_file, 'rb'), chunk_size),
                           content_type=mimetypes.guess_type(the_file)[0])
   response['Content-Length'] = os.path.getsize(the_file)    
   response['Content-Disposition'] = "attachment; filename=%s" % filename
   return response

【讨论】:

谢谢,这就像我想要实现的,我知道直接使用 Apache 提供服务,想知道如何使用 django 视图做得更好:) 感谢您的回答 这里文件生成成功,但下划线附加到文件名的末尾.....像 file_name.txt_ , name_view.pdf_ 等,所以如何避免这个下划线文件名的最后一个? 此解决方案适用于 Django 1.9,因为 FileWrapper 不再可用。 @azmeuk,使用 Django 1.9 from wsgiref.util import FileWrapper - 看看这里github.com/django/django/commit/… 请注意,除了StreamingHttpResponse,您还可以使用FileResponse:流式响应,专门用于文件。见docs.djangoproject.com/en/1.9/ref/request-response/…【参考方案2】:

您可以使用answer 中描述的“sendfile”方法。

实际上你需要这个(c&p):

response = HttpResponse(mimetype='application/force-download')
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(nginx 或lighty 也支持)

【讨论】:

【参考方案3】:

安装 GZipMiddleware(Django 1.4 及更低版本)后,FileWrapper 将不起作用: https://code.djangoproject.com/ticket/6027

如果使用 GZipMiddleware,一个实际的解决方案是像这样编写 FileWrapper 的子类:

from wsgiref.util import FileWrapper
class FixedFileWrapper(FileWrapper):
    def __iter__(self):
        self.filelike.seek(0)
        return self

import mimetypes, os
my_file = '/some/path/xy.ext'
response = HttpResponse(FixedFileWrapper(open(my_file, 'rb')), content_type=mimetypes.guess_type(my_file)[0])
response['Content-Length'] = os.path.getsize(my_file)
response['Content-Disposition'] = "attachment; filename=%s" % os.path.basename(my_file)
return response

从 Python 2.5 开始,无需从 Django 导入 FileWrapper。

【讨论】:

【参考方案4】:

除非您要处理的此类请求数量非常少,否则任何需要通过 django 提供内容的解决方案都无法扩展。对于将来要扩展的任何内容,您可能希望将内容存储和服务移至单独的服务器,然后这将不起作用。

推荐的方法是通过更轻量级的服务器(例如 nginx)来提供静态内容。为了增加安全性,通过设置 cookie 或通过 get 参数从 django 向静态服务器传递一个令牌。

令牌应具有以下值:时间戳、文件名、用户 ID。它应该由 django 应用程序通过一些密钥进行签名。

接下来,编写一个小型 nginx 模块来检查令牌以及用户是否确实可以访问该文件。它还应该通过检查时间戳来检查令牌是否不够老。

【讨论】:

【参考方案5】:

最好使用 FileRespose,它是 StreamingHttpResponse 的子类,针对二进制文件进行了优化。如果由 wsgi 服务器提供,它使用 wsgi.file_wrapper,否则它将文件以小块的形式流出。

import os
from django.http import FileResponse
from django.core.servers.basehttp import FileWrapper


def download_file(request):
    _file = '/folder/my_file.zip'
    filename = os.path.basename(_file)
    response = FileResponse(FileWrapper(file(filename, 'rb')), content_type='application/x-zip-compressed')
    response['Content-Disposition'] = "attachment; filename=%s" % _file
    return response

【讨论】:

对于 django 1.10 使用,从 wsgiref.util import FileWrapper

以上是关于在 Django 中提供大文件(高负载)的主要内容,如果未能解决你的问题,请参考以下文章

Haproxy负载均衡部署(高可用,高负载)!

Haproxy负载均衡部署(高可用,高负载)!

NFS v4 具有快速网络和平均 IOPS 磁盘。大文件传输时负载增加高

集群高可用之lvs

想要高可用?搞定负载均衡架构是关键

nginx实现高可用