如何用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中利用队列asyncio.Queue进行通讯详解
Sep 10 Python
对numpy和pandas中数组的合并和拆分详解
Apr 11 Python
PyQt4实现下拉菜单可供选择并打印出来
Apr 20 Python
python numpy 显示图像阵列的实例
Jul 02 Python
Python后台开发Django会话控制的实现
Apr 15 Python
Flask之pipenv虚拟环境的实现
Nov 26 Python
Python3基本输入与输出操作实例分析
Feb 14 Python
pycharm解决关闭flask后依旧可以访问服务的问题
Apr 03 Python
TensorFlow的reshape操作 tf.reshape的实现
Apr 19 Python
python利用文件时间批量重命名照片和视频
Feb 09 Python
Pyqt5将多个类组合在一个界面显示的完整示例
Sep 04 Python
Python可视化动图组件ipyvizzu绘制惊艳的可视化动图
Apr 21 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
PHP数字格式化
2006/12/06 PHP
php提示undefined index的几种解决方法
2012/05/21 PHP
简单的php数据库操作类代码(增,删,改,查)
2013/04/08 PHP
php缩放图片(根据宽高的等比例缩放)实例介绍
2013/06/09 PHP
php静态文件生成类实例分析
2015/01/03 PHP
PHP实现获取毫秒时间戳的方法【使用microtime()函数】
2019/03/01 PHP
Thinkphp5.0框架的Db操作实例分析【连接、增删改查、链式操作等】
2019/10/11 PHP
在百度知道团队中快速审批新成员的js脚本
2014/02/02 Javascript
TypeError document.getElementById(...) is null错误原因
2015/05/18 Javascript
js强制把网址设为默认首页
2015/09/29 Javascript
VueJs单页应用实现微信网页授权及微信分享功能示例
2017/07/26 Javascript
在Debian(Raspberry Pi)树莓派上安装NodeJS的教程详解
2017/09/19 NodeJs
浅谈VUE监听窗口变化事件的问题
2018/02/24 Javascript
Vue瀑布流插件的使用示例
2018/09/19 Javascript
JS实现十分钟倒计时代码实例
2018/10/18 Javascript
axios封装,使用拦截器统一处理接口,超详细的教程(推荐)
2019/05/02 Javascript
微信小程序点击列表跳转到对应详情页过程解析
2019/09/26 Javascript
详解javascript void(0)
2020/07/13 Javascript
JavaScript中layim之整合右键菜单的示例代码
2021/02/06 Javascript
[57:50]DOTA2上海特级锦标赛主赛事日 - 4 胜者组决赛Secret VS Liquid第二局
2016/03/05 DOTA
pycharm设置注释颜色的方法
2018/05/23 Python
Python使用tkinter库实现文本显示用户输入功能示例
2018/05/30 Python
Windows下PyCharm安装图文教程
2018/08/27 Python
利用Python将数值型特征进行离散化操作的方法
2018/11/06 Python
Python除法之传统除法、Floor除法及真除法实例详解
2019/05/23 Python
Python 类的私有属性和私有方法实例分析
2019/09/29 Python
使用 Python 写一个简易的抽奖程序
2019/12/08 Python
如何定义TensorFlow输入节点
2020/01/23 Python
python矩阵运算,转置,逆运算,共轭矩阵实例
2020/05/11 Python
英文求职信结束语大全
2013/10/26 职场文书
竞聘演讲稿范文
2014/01/12 职场文书
尊老爱亲美德少年事迹材料
2014/08/14 职场文书
立春观后感
2015/06/18 职场文书
uni-app 微信小程序授权登录的实现步骤
2022/02/18 Javascript
详解python的异常捕获
2022/03/03 Python
vmware虚拟机打不开vmx文件怎么办 ?vmware虚拟机vmx文件打开方法
2022/04/08 数码科技