如何用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遍历 truple list dictionary的几种方法总结
Sep 11 Python
Python字符串格式化%s%d%f详解
Feb 02 Python
Python2.7.10以上pip更新及其他包的安装教程
Jun 12 Python
Python使用win32com模块实现数据库表结构自动生成word表格的方法
Jul 17 Python
Python的iOS自动化打包实例代码
Nov 22 Python
梅尔倒谱系数(MFCC)实现
Jun 19 Python
pandas按行按列遍历Dataframe的几种方式
Oct 23 Python
TensorBoard 计算图的可视化实现
Feb 15 Python
Python换行与不换行的输出实例
Feb 19 Python
使用tensorflow根据输入更改tensor shape
Jun 23 Python
python计算auc的方法
Sep 09 Python
Python Django项目和应用的创建详解
Nov 27 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使用fgetcsv读取csv文件出现乱码的解决方法
2014/11/08 PHP
PHP中使用Imagick读取pdf并生成png缩略图实例
2015/01/21 PHP
PHP PDOStatement::fetchAll讲解
2019/01/31 PHP
PHP实现一个限制实例化次数的类示例
2019/09/16 PHP
php下的原生ajax请求用法实例分析
2020/02/28 PHP
dojo 之基础篇
2007/03/24 Javascript
jQuery)扩展jQuery系列之一 模拟alert,confirm(一)
2010/12/04 Javascript
javascript 正则表达式相关应介绍
2012/11/27 Javascript
jquery 列表双向选择器之改进版
2013/08/09 Javascript
JavaScript实现重置表单(reset)的方法
2015/04/02 Javascript
JavaScript File API实现文件上传预览
2016/02/02 Javascript
javascript比较语义化版本号的实现代码
2016/09/09 Javascript
jQuery插件HighCharts实现气泡图效果示例【附demo源码】
2017/03/13 Javascript
微信小程序静默登录的实现代码
2020/01/08 Javascript
javascript实现拼图游戏
2021/01/29 Javascript
[48:02]Ti4循环赛第三日 VG vs Liquid和NEWBEE vs DK
2014/07/12 DOTA
[56:29]Secret vs Optic 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
Python中使用tarfile压缩、解压tar归档文件示例
2015/04/05 Python
Python enumerate函数功能与用法示例
2019/03/01 Python
python的turtle库使用详解
2019/05/10 Python
Python read函数按字节(字符)读取文件的实现
2019/07/03 Python
python多进程下实现日志记录按时间分割
2019/07/22 Python
分享8点超级有用的Python编程建议(推荐)
2019/10/13 Python
CSS3实例分享--超炫checkbox复选框和radio单选框
2014/09/01 HTML / CSS
培训讲师邀请函
2014/01/10 职场文书
《日月潭》教学反思
2014/02/28 职场文书
青年志愿者活动总结
2014/04/26 职场文书
公民代理授权委托书
2014/09/24 职场文书
员工保密协议书
2014/09/27 职场文书
项目经理岗位职责范本
2015/04/01 职场文书
芙蓉镇观后感
2015/06/10 职场文书
灵魂歌王观后感
2015/06/17 职场文书
2015年秋季开学典礼校长致辞
2015/07/16 职场文书
Nginx的rewrite模块详解
2021/03/31 Servers
解决pycharm安装scrapy DLL load failed:找不到指定的程序的问题
2021/06/08 Python
如何使用注解方式实现 Redis 分布式锁
2022/07/23 Redis