web-dev-qa-db-ja.com

POST data with pythonはhttp-gzipまたはdeflate圧縮でlibを要求できますか?

python 2.7のrequest-moduleを使用して、変更できないサービスに大きなデータチャンクを投稿します。データはほとんどテキストであるため、大きいですが、かなり圧縮されます。サーバーはgzipまたはdeflateエンコードを受け入れますが、リクエストにPOSTを実行し、データを自動的に正しくエンコードするように指示する方法がわかりません。

これがどのように可能であるかを示す、利用可能な最小限の例はありますか?

12
AME
# 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)
10
KnightOrc

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)
14
Marco Grassi

これを機能させることはできませんが、準備されたリクエストに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)
2
Robᵩ

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)

2
James D

いくつかの非常に大きなファイルが並行してアップロードされていたため、投稿をチャンクにする必要がありました。これが私が思いついた解決策です。

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()
1
Rosco