如何用Django处理gzip数据流


Posted in Python onJanuary 29, 2021

最近在工作中遇到一个需求,就是要开一个接口来接收供应商推送的数据。项目采用的python的django框架,我是想也没想,就直接一梭哈,写出了如下代码:

class XXDataPushView(APIView):
  """
  接收xx数据推送
  """
		# ...
  @white_list_required
  def post(self, request, **kwargs):
    req_data = request.data or {}
				# ...

但随后,发现每日数据并没有任何变化,质问供应商是否没有做推送,在忽悠我们。然后对方给的答复是,他们推送的是gzip压缩的数据流,接收端需要主动进行解压。此前从没有处理过这种压缩的数据,对方具体如何做的推送对我来说也是一个黑盒。

因此,我要求对方给一个推送的简单示例,没想到对方不讲武德,仍过来一段没法单独运行的java代码:

private byte[] compress(JSONObject body) {
  try {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    GZIPOutputStream gzip = new GZIPOutputStream(out);
    gzip.write(body.toString().getBytes());
    gzip.close();
    return out.toByteArray();
  } catch (Exception e) {
    logger.error("Compress data failed with error: " + e.getMessage()).commit();
  }
  return JSON.toJSONString(body).getBytes();
}

public void post(JSONObject body, String url, FutureCallback<HttpResponse> callback) {
  RequestBuilder requestBuilder = RequestBuilder.post(url);
  requestBuilder.addHeader("Content-Type", "application/json; charset=UTF-8");
  requestBuilder.addHeader("Content-Encoding", "gzip");

  byte[] compressData = compress(body);

  int timeout = (int) Math.max(((float)compressData.length) / 5000000, 5000);

  RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
  requestConfigBuilder.setSocketTimeout(timeout).setConnectTimeout(timeout);

  requestBuilder.setEntity(new ByteArrayEntity(compressData));

  requestBuilder.setConfig(requestConfigBuilder.build());

  excuteRequest(requestBuilder, callback);
}

private void excuteRequest(RequestBuilder requestBuilder, FutureCallback<HttpResponse> callback) {
  HttpUriRequest request = requestBuilder.build();
  httpClient.execute(request, new FutureCallback<HttpResponse>() {
    @Override
    public void completed(HttpResponse httpResponse) {
      try {
        int responseCode = httpResponse.getStatusLine().getStatusCode();
        if (callback != null) {
          if (responseCode == 200) {
            callback.completed(httpResponse);
          } else {
            callback.failed(new Exception("Status code is not 200"));
          }
        }
      } catch (Exception e) {
        logger.error("Get error on " + requestBuilder.getMethod() + " " + requestBuilder.getUri() + ": " + e.getMessage()).commit();
        if (callback != null) {
          callback.failed(e);
        }
      }

      EntityUtils.consumeQuietly(httpResponse.getEntity());
    }

    @Override
    public void failed(Exception e) {
      logger.error("Get error on " + requestBuilder.getMethod() + " " + requestBuilder.getUri() + ": " + e.getMessage()).commit();
      if (callback != null) {
        callback.failed(e);
      }
    }

    @Override
    public void cancelled() {
      logger.error("Request cancelled on " + requestBuilder.getMethod() + " " + requestBuilder.getUri()).commit();
      if (callback != null) {
        callback.cancelled();
      }
    }
  });
}

从上述代码可以看出,对方将json数据压缩为了gzip数据流stream。于是搜索django的文档,只有这段关于gzip处理的装饰器描述:

django.views.decorators.gzip 里的装饰器控制基于每个视图的内容压缩。

  • gzip_page()

如果浏览器允许 gzip 压缩,那么这个装饰器将压缩内容。它相应的设置了 Vary 头部,这样缓存将基于 Accept-Encoding 头进行存储。

但是,这个装饰器只是压缩请求响应至浏览器的内容,我们目前的需求是解压缩接收的数据。这不是我们想要的。

幸运的是,在flask中有一个扩展叫flask-inflate,安装了此扩展会自动对请求来的数据做解压操作。查看该扩展的具体代码处理:

# flask_inflate.py
import gzip
from flask import request

GZIP_CONTENT_ENCODING = 'gzip'


class Inflate(object):
  def __init__(self, app=None):
    if app is not None:
      self.init_app(app)

  @staticmethod
  def init_app(app):
    app.before_request(_inflate_gzipped_content)


def inflate(func):
  """
  A decorator to inflate content of a single view function
  """
  def wrapper(*args, **kwargs):
    _inflate_gzipped_content()
    return func(*args, **kwargs)

  return wrapper


def _inflate_gzipped_content():
  content_encoding = getattr(request, 'content_encoding', None)

  if content_encoding != GZIP_CONTENT_ENCODING:
    return

  # We don't want to read the whole stream at this point.
  # Setting request.environ['wsgi.input'] to the gzipped stream is also not an option because
  # when the request is not chunked, flask's get_data will return a limited stream containing the gzip stream
  # and will limit the gzip stream to the compressed length. This is not good, as we want to read the
  # uncompressed stream, which is obviously longer.
  request.stream = gzip.GzipFile(fileobj=request.stream)

上述代码的核心是:

request.stream = gzip.GzipFile(fileobj=request.stream)

于是,在django中可以如下处理:

class XXDataPushView(APIView):
  """
  接收xx数据推送
  """
		# ...
  @white_list_required
  def post(self, request, **kwargs):
    content_encoding = request.META.get("HTTP_CONTENT_ENCODING", "")
    if content_encoding != "gzip":
      req_data = request.data or {}
    else:
      gzip_f = gzip.GzipFile(fileobj=request.stream)
      data = gzip_f.read().decode(encoding="utf-8")
      req_data = json.loads(data)
    # ... handle req_data

ok, 问题完美解决。还可以用如下方式测试请求:

import gzip
import requests
import json

data = {}

data = json.dumps(data).encode("utf-8")
data = gzip.compress(data)

resp = requests.post("http://localhost:8760/push_data/",data=data,headers={"Content-Encoding": "gzip", "Content-Type":"application/json;charset=utf-8"})

print(resp.json())

以上就是如何用Django处理gzip数据流的详细内容,更多关于Django处理gzip数据流的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python监控网卡流量并使用graphite绘图的示例
Apr 27 Python
Python的subprocess模块总结
Nov 07 Python
python实现的简单FTP上传下载文件实例
Jun 30 Python
python用pickle模块实现“增删改查”的简易功能
Jun 07 Python
Python实现在tkinter中使用matplotlib绘制图形的方法示例
Jan 18 Python
python3.4爬虫demo
Jan 22 Python
flask应用部署到服务器的方法
Jul 12 Python
python opencv图片编码为h264文件的实例
Dec 12 Python
python新式类和经典类的区别实例分析
Mar 23 Python
Python如何批量获取文件夹的大小并保存
Mar 31 Python
python模拟哔哩哔哩滑块登入验证的实现
Apr 24 Python
能让Python提速超40倍的神器Cython详解
Jun 24 Python
Spy++的使用方法及下载教程
Jan 29 #Python
Python实现随机爬山算法
Jan 29 #Python
用pushplus+python监控亚马逊到货动态推送微信
Jan 29 #Python
用python监控服务器的cpu,磁盘空间,内存,超过邮件报警
Jan 29 #Python
python热力图实现简单方法
Jan 29 #Python
Ubuntu20.04环境安装tensorflow2的方法步骤
Jan 29 #Python
python3定位并识别图片验证码实现自动登录功能
Jan 29 #Python
You might like
日本因肺炎疫情影响,这几部动漫推延播放!
2020/03/03 日漫
Discuz! Passport 通行证整合
2008/03/27 PHP
通过dbi使用perl连接mysql数据库的方法
2014/04/16 PHP
php实现俄罗斯乘法实例
2015/03/07 PHP
PHP Class SoapClient not found解决方法
2018/01/20 PHP
需要做特殊处理的DOM元素属性的访问
2010/11/05 Javascript
解析jquery获取父窗口的元素
2013/06/26 Javascript
jQuery向上遍历DOM树之parents(),parent(),closest()之间的区别
2013/12/02 Javascript
javascript中取前n天日期的两种方法分享
2014/01/26 Javascript
jQuery中removeAttr()方法用法实例
2015/01/05 Javascript
jQuery实现的仿select功能代码
2015/08/19 Javascript
javascript点击按钮实现隐藏显示切换效果
2016/02/03 Javascript
JS实现的自定义显示加载等待图片插件(loading.gif)
2016/06/17 Javascript
Web安全测试之XSS实例讲解
2016/08/15 Javascript
基于SpringMVC+Bootstrap+DataTables实现表格服务端分页、模糊查询
2016/10/30 Javascript
chorme 浏览器记住密码后input黄色背景处理方法(两种)
2017/11/22 Javascript
JS中‘hello’与new String(‘hello’)引出的问题详解
2018/08/14 Javascript
详解如何创建并发布一个 vue 组件
2018/11/08 Javascript
python模拟登陆阿里妈妈生成商品推广链接
2014/04/03 Python
Python中使用SAX解析xml实例
2014/11/21 Python
python开发利器之ulipad的使用实践
2017/03/16 Python
读取本地json文件,解析json(实例讲解)
2017/12/06 Python
用Python实现筛选文件脚本的方法
2018/10/27 Python
python将txt等文件中的数据读为numpy数组的方法
2018/12/22 Python
jupyter notebook 恢复误删单元格或者历史代码的实现
2020/04/17 Python
vscode写python时的代码错误提醒和自动格式化的方法
2020/05/07 Python
手把手教你用纯css3实现轮播图效果实例
2017/05/04 HTML / CSS
巴基斯坦购物网站:Goto
2019/03/11 全球购物
函授大专自我鉴定
2013/11/01 职场文书
公司任命书模板
2014/06/06 职场文书
创优争先心得体会
2014/09/11 职场文书
用python开发一款操作MySQL的小工具
2021/05/12 Python
微软Win11有哪些隐藏功能? windows11多个功能汇总
2021/11/21 数码科技
Python FuzzyWuzzy实现模糊匹配
2022/04/28 Python
Java 定时任务技术趋势简介
2022/05/04 Java/Android
详解Mysql数据库平滑扩容解决高并发和大数据量问题
2022/05/25 MySQL