
前言日常开发中使用 Python 调用 HTTP POST 接口时如果上传报文体积较大大量 JSON、批量数据、文本内容未压缩的数据会占用更多带宽增加网络耗时高并发场景下还会提升服务器流量压力。很多开发者熟悉服务端返回压缩响应Accept-Encoding却很少了解客户端同样可以将请求 Body 压缩后上传。本文讲解 HTTP 请求体压缩原理提供requests、httpx、aiohttp完整实现代码并梳理常见踩坑点。关键概念区分Accept-Encoding: gzip, deflate告诉服务端客户端支持接收压缩响应下行压缩绝大多数库自动支持Content-Encoding: gzip告诉服务端当前 POST 请求 Body已经被压缩上行压缩需要手动实现⚠️ 重要前提服务端必须实现对应解压逻辑。若后端没有处理Content-Encoding请求头直接接收压缩二进制流会出现解析报错。一、实现原理将原始报文JSON/二进制文本序列化为字节使用 gzip/deflate 算法压缩字节数据请求头增加Content-Encoding: gzipPOST 使用data/content参数传入压缩后的二进制禁止使用 json 参数HTTP 自动填充Content-Length无需手动计算。推荐算法gzip跨语言、各后端框架兼容性最优。二、requests 同步实现最常用importrequestsimportgzipimportjsonfromioimportBytesIOdefgzip_compress(raw_data:bytes)-bytes:gzip压缩字节数据bufferBytesIO()withgzip.GzipFile(fileobjbuffer,modewb)asfp:fp.write(raw_data)returnbuffer.getvalue()if__name____main__:urlhttp://127.0.0.1:8000/api/uploadpayload{list:list(range(2000)),content:大量测试文本*500}# 序列化为JSON字节raw_bytesjson.dumps(payload,ensure_asciiFalse).encode(utf-8)# 阈值判断过小的数据不压缩避免压缩头部导致体积变大compress_threshold2048headers{Content-Type:application/json; charsetutf-8}iflen(raw_bytes)compress_threshold:bodygzip_compress(raw_bytes)headers[Content-Encoding]gzipelse:bodyraw_bytes resprequests.post(url,headersheaders,databody)print(状态码,resp.status_code)print(响应内容,resp.text)避坑不要使用requests.post(..., jsonpayload)json 参数内部会自动编码无法传入压缩后的二进制数据必须使用data。三、httpx 同步/异步实现新项目推荐importhttpximportgzipimportjsonfromioimportBytesIOdefgzip_compress(raw_data:bytes)-bytes:bufferBytesIO()withgzip.GzipFile(fileobjbuffer,modewb)asfp:fp.write(raw_data)returnbuffer.getvalue()defsync_request():urlhttp://127.0.0.1:8000/api/uploadpayload{data:list(range(3000))}raw_bytesjson.dumps(payload).encode(utf-8)compressedgzip_compress(raw_bytes)headers{Content-Type:application/json; charsetutf-8,Content-Encoding:gzip}withhttpx.Client()asclient:rclient.post(url,headersheaders,contentcompressed)print(r.status_code,r.text)# 异步版本importasyncioasyncdefasync_request():urlhttp://127.0.0.1:8000/api/uploadpayload{data:list(range(3000))}raw_bytesjson.dumps(payload).encode(utf-8)compressedgzip_compress(raw_bytes)headers{Content-Type:application/json; charsetutf-8,Content-Encoding:gzip}asyncwithhttpx.AsyncClient()asclient:rawaitclient.post(url,headersheaders,contentcompressed)print(r.status_code,r.text)if__name____main__:sync_request()# asyncio.run(async_request())四、aiohttp 异步实现importaiohttpimportasyncioimportgzipimportjsonfromioimportBytesIOdefgzip_compress(raw_data:bytes)-bytes:bufferBytesIO()withgzip.GzipFile(fileobjbuffer,modewb)asfp:fp.write(raw_data)returnbuffer.getvalue()asyncdefmain():urlhttp://127.0.0.1:8000/api/uploadpayload{data:list(range(3000))}raw_bytesjson.dumps(payload).encode(utf-8)compressed_bodygzip_compress(raw_bytes)headers{Content-Type:application/json; charsetutf-8,Content-Encoding:gzip}asyncwithaiohttp.ClientSession()assession:asyncwithsession.post(url,datacompressed_body,headersheaders)asresp:print(status:,resp.status)print(awaitresp.text())if__name____main__:asyncio.run(main())五、配套服务端简易解压示例FastAPI测试时需要后端支持解压附上最简示例fromfastapiimportFastAPI,RequestimportgzipimportjsonfromioimportBytesIO appFastAPI()app.post(/api/upload)asyncdefupload(request:Request):headersrequest.headers bodyawaitrequest.body()# 判断是否gzip压缩ifheaders.get(Content-Encoding)gzip:bufBytesIO(body)withgzip.GzipFile(fileobjbuf)asf:bodyf.read()datajson.loads(body)return{msg:接收成功,data:data}六、常见问题与优化建议1. 小数据不要强行压缩gzip 自带固定头部十几字节报文很小的时候压缩后体积可能大于原始数据。建议设置阈值2KB左右超过阈值再开启压缩。2. Content-Type 不要删除Content-Encoding只是标记编码方式不能替代 Content-Type。JSON 请求依旧保留application/json。3. 代理、网关兼容性问题部分老旧反向代理、防火墙不识别Content-Encoding可能直接丢弃请求。上线前务必做连通性测试。4. 区分上行压缩与下行压缩很多人混淆两个头Accept-Encoding客户端声明能接收压缩响应下载Content-Encoding请求体已经压缩上传requests、httpx 默认自带 Accept-Encoding响应自动解压无需手动处理。5. 二进制文件场景不仅限于 JSON上传文本、CSV 等均可使用同样逻辑如果是图片、视频等本身已压缩的文件再次 gzip 收益极低不建议二次压缩。七、适用场景总结✅ 适合批量上报接口、大量JSON文本、日志上报、大数据同步❌ 不适合极小报文、图片/视频等已压缩媒体文件当接口传输量大、网络带宽有限、跨机房调用时开启请求体压缩能有效降低传输耗时减少流量开销。