如何用Django处理gzip数据流
最近在工作中遇到一个需求,就是要开一个接口来接收供应商推送的数据。项目采用的python的django框架,我是想也没想,就直接一梭哈,写出了如下代码:
classXXDataPushView(APIView):
"""
接收xx数据推送
"""
#...
@white_list_required
defpost(self,request,**kwargs):
req_data=request.dataor{}
#...
但随后,发现每日数据并没有任何变化,质问供应商是否没有做推送,在忽悠我们。然后对方给的答复是,他们推送的是gzip压缩的数据流,接收端需要主动进行解压。此前从没有处理过这种压缩的数据,对方具体如何做的推送对我来说也是一个黑盒。
因此,我要求对方给一个推送的简单示例,没想到对方不讲武德,仍过来一段没法单独运行的java代码:
privatebyte[]compress(JSONObjectbody){
try{
ByteArrayOutputStreamout=newByteArrayOutputStream();
GZIPOutputStreamgzip=newGZIPOutputStream(out);
gzip.write(body.toString().getBytes());
gzip.close();
returnout.toByteArray();
}catch(Exceptione){
logger.error("Compressdatafailedwitherror:"+e.getMessage()).commit();
}
returnJSON.toJSONString(body).getBytes();
}
publicvoidpost(JSONObjectbody,Stringurl,FutureCallbackcallback){
RequestBuilderrequestBuilder=RequestBuilder.post(url);
requestBuilder.addHeader("Content-Type","application/json;charset=UTF-8");
requestBuilder.addHeader("Content-Encoding","gzip");
byte[]compressData=compress(body);
inttimeout=(int)Math.max(((float)compressData.length)/5000000,5000);
RequestConfig.BuilderrequestConfigBuilder=RequestConfig.custom();
requestConfigBuilder.setSocketTimeout(timeout).setConnectTimeout(timeout);
requestBuilder.setEntity(newByteArrayEntity(compressData));
requestBuilder.setConfig(requestConfigBuilder.build());
excuteRequest(requestBuilder,callback);
}
privatevoidexcuteRequest(RequestBuilderrequestBuilder,FutureCallbackcallback){
HttpUriRequestrequest=requestBuilder.build();
httpClient.execute(request,newFutureCallback(){
@Override
publicvoidcompleted(HttpResponsehttpResponse){
try{
intresponseCode=httpResponse.getStatusLine().getStatusCode();
if(callback!=null){
if(responseCode==200){
callback.completed(httpResponse);
}else{
callback.failed(newException("Statuscodeisnot200"));
}
}
}catch(Exceptione){
logger.error("Geterroron"+requestBuilder.getMethod()+""+requestBuilder.getUri()+":"+e.getMessage()).commit();
if(callback!=null){
callback.failed(e);
}
}
EntityUtils.consumeQuietly(httpResponse.getEntity());
}
@Override
publicvoidfailed(Exceptione){
logger.error("Geterroron"+requestBuilder.getMethod()+""+requestBuilder.getUri()+":"+e.getMessage()).commit();
if(callback!=null){
callback.failed(e);
}
}
@Override
publicvoidcancelled(){
logger.error("Requestcancelledon"+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 importgzip fromflaskimportrequest GZIP_CONTENT_ENCODING='gzip' classInflate(object): def__init__(self,app=None): ifappisnotNone: self.init_app(app) @staticmethod definit_app(app): app.before_request(_inflate_gzipped_content) definflate(func): """ Adecoratortoinflatecontentofasingleviewfunction """ defwrapper(*args,**kwargs): _inflate_gzipped_content() returnfunc(*args,**kwargs) returnwrapper def_inflate_gzipped_content(): content_encoding=getattr(request,'content_encoding',None) ifcontent_encoding!=GZIP_CONTENT_ENCODING: return #Wedon'twanttoreadthewholestreamatthispoint. #Settingrequest.environ['wsgi.input']tothegzippedstreamisalsonotanoptionbecause #whentherequestisnotchunked,flask'sget_datawillreturnalimitedstreamcontainingthegzipstream #andwilllimitthegzipstreamtothecompressedlength.Thisisnotgood,aswewanttoreadthe #uncompressedstream,whichisobviouslylonger. request.stream=gzip.GzipFile(fileobj=request.stream)
上述代码的核心是:
request.stream=gzip.GzipFile(fileobj=request.stream)
于是,在django中可以如下处理:
classXXDataPushView(APIView):
"""
接收xx数据推送
"""
#...
@white_list_required
defpost(self,request,**kwargs):
content_encoding=request.META.get("HTTP_CONTENT_ENCODING","")
ifcontent_encoding!="gzip":
req_data=request.dataor{}
else:
gzip_f=gzip.GzipFile(fileobj=request.stream)
data=gzip_f.read().decode(encoding="utf-8")
req_data=json.loads(data)
#...handlereq_data
ok,问题完美解决。还可以用如下方式测试请求:
importgzip
importrequests
importjson
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数据流的资料请关注毛票票其它相关文章!