我可以用带有http-gzip或deflate压缩的python请求lib来发布数据吗?

时间:2013-12-06 14:08:54

标签: python http gzip deflate

我使用python 2.7的请求模块将更大块的数据发布到我无法更改的服务中。由于数据主要是文本,因此数据量很大但压缩效果会很好。服务器会接受gzip或deflate-encoding,但是我不知道如何指示请求执行POST并自动正确编码数据。

是否有可用的最小示例,说明了这是如何实现的?

6 个答案:

答案 0 :(得分:13)

我已经测试了Robᵩ提出的解决方案并进行了一些修改,但它确实有效。

PSEUDOCODE(对不起,我已经从我的代码中推断出来了,所以我不得不剪掉一些零件并且没有经过测试,无论如何你可以得到你的想法)

additional_headers['content-encoding'] = 'gzip'
s = StringIO.StringIO()
g = gzip.GzipFile(fileobj=s, mode='w')
g.write(json_body)
g.close()
gzipped_body = s.getvalue()
request_body = gzipped_body

r = requests.post(endpoint_url, data=request_body, headers=additional_headers)

答案 1 :(得分:8)

# Works if backend supports gzip

additional_headers['content-encoding'] = 'gzip'
request_body = zlib.compress(json.dumps(post_data))
r = requests.post('http://post.example.url', data=request_body, headers=additional_headers)

答案 2 :(得分:2)

我无法使其工作,但您可以将gzip数据插入准备好的请求中:

#UNPROVEN
r=requests.Request('POST', 'http://httpbin.org/post', data={"hello":"goodbye"})
p=r.prepare()
s=StringIO.StringIO()
g=gzip.GzipFile(fileobj=s,mode='w')
g.write(p.body)
g.close()
p.body=s.getvalue()
p.headers['content-encoding']='gzip'
p.headers['content-length'] = str(len(p.body))  # Not sure about this
r=requests.Session().send(p)

答案 3 :(得分:1)

对于python 3:

from io import BytesIO
import gzip

def zip_payload(payload: str) -> bytes:
    btsio = BytesIO()
    g = gzip.GzipFile(fileobj=btsio, mode='w')
    g.write(bytes(payload, 'utf8'))
    g.close()
    return btsio.getvalue()

headers = {
    'Content-Encoding': 'gzip'
}
zipped_payload = zip_payload(payload)
requests.post(url, zipped_payload, headers=headers)

答案 4 :(得分:1)

我需要对帖子进行分块,因为我有多个非常大的文件同时上传。这是我想出的解决方案。

import requests
import zlib

"""Generator that reads a file in chunks and compresses them"""
def chunked_read_and_compress(file_to_send, zlib_obj, chunk_size):
    compression_incomplete = True
    with open(file_to_send,'rb') as f:
        # The zlib might not give us any data back, so we have nothing to yield, just
        # run another loop until we get data to yield.
        while compression_incomplete:
            plain_data = f.read(chunk_size)
            if plain_data:
                compressed_data = zlib_obj.compress(plain_data)
            else:
                compressed_data = zlib_obj.flush()
                compression_incomplete = False
            if compressed_data:
                yield compressed_data

"""Post a file to a url that is content-encoded gzipped compressed and chunked (for large files)"""
def post_file_gzipped(url, file_to_send, chunk_size=5*1024*1024, compress_level=6, headers={}, requests_kwargs={}):
    headers_to_send = {'Content-Encoding': 'gzip'}
    headers_to_send.update(headers)
    zlib_obj = zlib.compressobj(compress_level, zlib.DEFLATED, 31)
    return requests.post(url, data=chunked_read_and_compress(file_to_send, zlib_obj, chunk_size), headers=headers_to_send, **requests_kwargs)

resp = post_file_gzipped('http://httpbin.org/post', 'somefile')
resp.raise_for_status()

答案 5 :(得分:1)

由于标题不正确或丢失,接受的答案可能是错误的:

additional_headers['content-encoding'] = 'gzip'
request_body = zlib.compress(json.dumps(post_data))

使用 zlib 模块的 compressobj 方法提供 wbits 参数来指定标头格式应该可以工作。 默认值为 MAX_WBITS=15,表示 zlib 标头格式。这对于 Content-Encoding: deflate 是正确的。 对于 compress 方法,此参数不可用,并且文档没有提及不幸使用了哪个标头(如果有)。

对于 Content-Encoding: gzipwbits 应该介于 16 + (9 to 15) 之间,所以 16+zlib.MAX_WBITS 将是一个不错的选择。

我检查了 urllib3 如何解码这两种情况下的响应,它为 deflate 实现了一个试错机制(它尝试原始和 zlib 标头格式)。这可以解释为什么有些人对其他人没有的已接受答案的解决方案存在问题。


tl;博士

gzip

additional_headers['Content-Encoding'] = 'gzip'
compress = zlib.compressobj(wbits=16+zlib.MAX_WBITS)
body = compress.compress(data) + compress.flush()

放气

additional_headers['Content-Encoding'] = 'deflate'
compress = zlib.compressobj()
body = compress.compress(data) + compress.flush()