如何用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,FutureCallback callback){ 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数据流的资料请关注毛票票其它相关文章!