如何用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实现获取客户机上指定文件并传输到服务器的方法
Mar 16 Python
Python作用域用法实例详解
Mar 15 Python
TensorFlow实现Softmax回归模型
Mar 09 Python
对python模块中多个类的用法详解
Jan 10 Python
python pandas获取csv指定行 列的操作方法
Jul 12 Python
Linux下通过python获取本机ip方法示例
Sep 06 Python
pycharm中import呈现灰色原因的解决方法
Mar 04 Python
sklearn中的交叉验证的实现(Cross-Validation)
Feb 22 Python
详解Python小数据池和代码块缓存机制
Apr 07 Python
在pyCharm中下载第三方库的方法
Apr 18 Python
pytorch model.cuda()花费时间很长的解决
Jun 01 Python
Python代码实现双链表
May 25 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超级全局变量
2010/01/26 PHP
php遍历目录方法小结
2015/03/10 PHP
js模拟类继承小例子
2010/07/17 Javascript
js截取小数点后几位的写法
2013/11/14 Javascript
node.js中的fs.truncate方法使用说明
2014/12/15 Javascript
JavaScript实现定时隐藏与显示图片的方法
2015/08/06 Javascript
javaScript实现可缩放的显示区效果代码
2015/10/26 Javascript
实例详解jQuery结合GridView控件的使用方法
2016/01/04 Javascript
Angularjs CURD 详解及实例代码
2016/09/14 Javascript
jQuery实现的背景颜色渐变动画效果示例
2017/03/24 jQuery
vuejs如何配置less
2017/04/25 Javascript
微信小程序实现tab切换效果
2017/11/21 Javascript
在vscode中统一vue编码风格的方法
2018/02/22 Javascript
浅谈ajax请求不同页面的微信JSSDK问题
2018/02/26 Javascript
JS点击动态添加标签、删除指定标签的代码
2018/04/18 Javascript
微信小程序中如何计算距离某个节日还有多少天
2019/07/15 Javascript
node crawler如何添加promise支持
2020/02/01 Javascript
Python多线程结合队列下载百度音乐的方法
2015/07/27 Python
python UNIX_TIMESTAMP时间处理方法分析
2016/04/18 Python
Python正则简单实例分析
2017/03/21 Python
分享给Python新手们的几道简单练习题
2017/09/21 Python
详解Python自建logging模块
2018/01/29 Python
python3+PyQt5使用数据库表视图
2018/04/24 Python
Python中面向对象你应该知道的一下知识
2019/07/10 Python
Python实现实时数据采集新型冠状病毒数据实例
2020/02/04 Python
解决使用python print打印函数返回值多一个None的问题
2020/04/09 Python
canvas进阶之贝塞尔公式推导与物体跟随复杂曲线的轨迹运动
2018/01/10 HTML / CSS
英国伦敦的睡衣品牌:Asceno
2019/10/06 全球购物
加拿大留学自荐信
2014/01/28 职场文书
竞选班长自荐书范文
2014/03/09 职场文书
对祖国的寄语大全
2014/04/11 职场文书
我的中国梦演讲稿高中篇
2014/08/19 职场文书
教师师德考核自我评价
2014/09/13 职场文书
python某漫画app逆向
2021/03/31 Python
python pygame入门教程
2021/06/01 Python
阿里面试Nacos配置中心交互模型是push还是pull原理解析
2022/07/23 Java/Android