【问题标题】:Serving gzipped content from django从 django 提供 gzip 压缩的内容
【发布时间】:2010-10-31 18:00:51
【问题描述】:

我正在尝试在 Django 中提供文本/html 页面的 gzip 版本,但 Firefox 告诉我存在内容编码错误。

注意事项:

  • 我意识到这不是最佳实践,我很可能会使用 mod_gzip。这只是一个了解正在发生的事情的学习练习。
  • 我知道 Django gzip 中间件——它有二进制文件的问题。

这是我的代码:

rendered_page =  zlib.compress(template.render(context).encode('utf-8'))

response = HttpResponse(rendered_page)
response['Content-Encoding'] = 'gzip'
response['Content-Length'] = len(rendered_page)
return response

我在这里遗漏了什么吗?内容长度是否可能错误?我还缺少其他标题吗?

谢谢。

【问题讨论】:

  • 顺便说一句...如果您使用的是 Apache 2.x,请查看 mod_deflate...它比 mod_zip 更高级。

标签: django http gzip


【解决方案1】:

zlib 对于这个目的来说有点太低级了。以下是 GZip 中间件本身的工作方式(参见 django.utils.text.py 中的 compress_string):

import cStringIO, gzip
zbuf = cStringIO.StringIO()
zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuf)
zfile.write(template.render(context).encode('utf-8'))
zfile.close()

compressed_content = zbuf.getvalue()
response = HttpResponse(compressed_content)
response['Content-Encoding'] = 'gzip'
response['Content-Length'] = str(len(compressed_content))
return response

GZip 使用 zlib,但它自己的 zlib 生成的内容对于将“gzip”视为内容编码的浏览器来说编码不正确。希望对您有所帮助!

【讨论】:

    【解决方案2】:

    你也可以简单地使用Django's GZip Middleware:

    通过在 settings.py 中添加中间件来启用:

    MIDDLEWARE_CLASSES = (
        django.middleware.gzip.GZipMiddleware,
        ...
    )
    

    或者在您返回特定响应之前执行此操作。在您的 views.py 中,dec 将是某个 url 的处理程序

    from django.middleware.gzip import GZipMiddleware
    
    gzip_middleware = GZipMiddleware()
    
     def dec(request, *args, **kwargs):
            response = func(request, *args, **kwargs)
            return gzip_middleware.process_response(request, response)
            return dec
    

    注意:在使用 GZip 中间件之前,您应该确定不会受到旁道攻击。

    警告

    安全研究人员最近透露,当压缩 网站上使用了技术(包括 GZipMiddleware),该网站 可能会受到许多可能的攻击。使用前 GZipMiddleware 在您的网站上,您应该非常仔细地考虑 您是否受到这些攻击。 如果您对 无论您是否受到影响,都应避免使用 GZipMiddleware。 对于 更多详细信息,请参阅 BREACH 论文 (PDF) 和 bypassattack.com。

    还有:

    Django 1.10 中的更改:在旧版本中,Django 的 CSRF 保护 使用压缩时,机制容易受到 BREACH 攻击。 现在不再是这种情况,但您仍应注意不要 以这种方式泄露你自己的秘密。

    【讨论】:

    • 请注意当前有关 gzip 中间件的安全警告:docs.djangoproject.com/en/1.9/ref/middleware/…
    • 当我尝试这个时,我得到了No module named middlware.gzip。在我的视图方法顶部添加@gzip_page虽然有效。知道问题是什么吗?我在 Django 1.9
    • @hd1 您应该使用字符串来指定中间件类,而不是类/模块本身。所以你应该这样做而不是MIDDLEWARE_CLASSES = [django.middleware.gzip.GZipMiddleware,]MIDDLEWARE_CLASSES = ["django.middleware.gzip.GZipMiddleware",]
    • 从 1.10 开始,Django 包含了缓解 BREACH 攻击的方法,解决了上面提到的 Tobias 安全问题。 docs.djangoproject.com/en/1.11/ref/middleware
    • 由于安全问题,如上所述,我会采用装饰器方法。
    【解决方案3】:

    如果您正在压缩单个页面,而不是所有页面,则可以使用 gzip_page 装饰器而不是 GzipMiddleware。

    from django.views.decorators.gzip import gzip_page
    
    @gzip_page
    def viewFunc(request):
      return HttpResponse("hello"*100)
    

    参考这里:https://docs.djangoproject.com/en/1.4/topics/http/decorators/#module-django.views.decorators.gzip

    【讨论】:

    • 顺便说一句,这对于 Django 1.11.x 仍然有效,对于 Django 问题,恕我直言,Django 解决方案应该是公认的答案。
    【解决方案4】:

    为了其他人发现这个问题以及使用 nginx 的人,这个 SO 对我有用:

    https://*.com/a/41820704/4533488

    基本上在 /etc/nginx/nginx.conf 文件中打开 gzip 为我完成了所有压缩处理。在客户端,大多数现代浏览器在接收数据时会自动处理提取(解压缩)数据 - 太好了!

    这里是 nginx.conf 文件设置:

        http {
    
            #... other settings ...#
    
            ##
            # Gzip Settings
            ##
    
            gzip on;
            gzip_disable "msie6";
    
            gzip_vary on;
            gzip_proxied any;
            gzip_comp_level 6;
            gzip_buffers 16 8k;
            gzip_http_version 1.1;
            gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
        }
    

    【讨论】:

      【解决方案5】:

      如果您需要它用于单个页面并且您正在使用基于类的视图,请使用:

      gzip_middleware = GZipMiddleware()
      
      class GZipMixin(object):
      
          def dispatch(self, request, *args, **kwargs):
              response = super(GZipMixin, self).dispatch(request, *args, **kwargs)
              return gzip_middleware.process_response(request, response)
      

      然后在你的实际视图中:

      class MyView(GZipMixin, View):
          def get(self, request, *args, **kwargs):
               #return your response
      

      【讨论】:

        【解决方案6】:

        如果使用zlib 压缩数据,则必须将Content-Encoding 设置为deflate,而不是gzip

        rendered_page =  zlib.compress(template.render(context).encode('utf-8'))
        
        response = HttpResponse(rendered_page)
        response['Content-Encoding'] = 'deflate'
        response['Content-Length'] = len(rendered_page)
        return response
        

        Content-Encoding

        (...)

        放气

        使用 zlib 结构(在 RFC 1950 中定义)和 deflate 压缩算法(在 RFC 1951 中定义)。

        【讨论】: