(这个问题不是关于从web服务器透明解压缩gzip
编码的响应的问题;我知道requests
会自动处理这个问题。)
问题
我正在尝试将文件POST到RESTful web服务。显然,requests
使这项工作变得非常容易:
files = dict(data=(fn, file))
response = session.post(endpoint_url, files=files)
在这种情况下,我的文件是一种非常可压缩的格式(是的,XML),所以我想确保请求主体被压缩。
服务器声称接受gzip编码(响应头中的Accept-Encoding: gzip
),所以我应该能够对整个主体请求体进行gzip,对吧?
尝试的解决方案
以下是我的尝试:我首先构造请求并准备它,然后进入PreparedRequest
对象,拉出body
,通过gzip
运行它,然后将其放回。(哦,别忘了更新Content-Length
和Content-Encoding
标头。)
files = dict(data=(fn, file))
request = request.Request('POST',endpoint_url, files=files)
prepped = session.prepare_request(request)
with NamedTemporaryFile(delete=True) as gzfile:
gzip.GzipFile(fileobj=gzfile, mode="wb").write(prepped.body)
prepped.headers['Content-Length'] = gzfile.tell()
prepped.headers['Content-Encoding'] = 'gzip'
gzfile.seek(0,0)
prepped.body = gzfile.read()
response = session.send(prepped)
不幸的是,服务器不配合并返回500 Internal Server Error
。也许它真的不接受gzip
编码的请求?
或者我的方法可能有错误?这看起来相当复杂。有没有一种更简单的方法可以用python-requests
进行请求身体压缩?
编辑:修复了@sigmavirus24答案中的(3)和(5)(这些基本上只是我在简化代码以将其发布到此处时忽略的工件)。
或者我的方法可能有错误?
坦率地说,我不确定你是如何得出这种方法的,但肯定有一种更简单的方法。
首先,几件事:
- CCD_ 13参数构造了一个CCD_。因此,您正在压缩服务器可能不知道的内容
- CCD_ 15和CCD_。您需要
Transfer-Encoding
- 您不需要在
NamedTemporaryFile
上设置后缀 - 由于您没有明确提到您正在尝试压缩
multipart/form-data
请求,我假设您实际上并不想这样做 - 您对
session.Request
(我认为应该是requests.Request
)的调用缺少一个方法,即应该是:requests.Request('POST', endpoint_url, ...)
有了这些,我将如何做到这一点:
# Assuming `file` is a file-like obj
with NamedTemporaryFile(delete=True) as gzfile:
gzip.GzipFile(fileobj=gzfile, mode="wb").write(file.read())
headers = {'Content-Length': str(gzfile.tell()),
'Transfer-Encoding': 'gzip'}
gzfile.seek(0, 0)
response = session.post(endpoint_url, data=gzfile,
headers=headers)
假设file
中有xml
内容,并且您的意思只是压缩它,这应该对您有效。您可能想要设置一个Content-Type
标头,例如,您只需要进行
headers = {'Content-Length': gzfile.tell(),
'Content-Type': 'application/xml', # or 'text/xml'
'Transfer-Encoding': 'gzip'}
Transfer-Encoding
告诉服务器请求只在传输过程中被压缩,它应该对其进行解压缩。Content-Type
告诉服务器一旦处理了Transfer-Encoding
,如何处理内容。
我有一个问题被标记为完全重复。我对交易的两端都很关心。
sigmavirus24中的代码不是直接的剪切粘贴修复,但它是这个版本的灵感来源。
以下是我的解决方案的结果:
从python端发送
import json
import requests
import StringIO
import gzip
url = "http://localhost:3000"
headers = {"Content-Type":"application/octet-stream"}
data = [{"key": 1,"otherKey": "2"},
{"key": 3,"otherKey": "4"}]
payload = json.dumps(data)
out = StringIO.StringIO()
with gzip.GzipFile(fileobj=out, mode="w") as f:
f.write(json.dumps(data))
out.getvalue()
r = requests.post(url+"/zipped", data=out.getvalue(), headers=headers)
在快递端收货
var zlib = require("zlib");
var rawParser = bodyParser.raw({type: '*/*'});
app.post('/zipped', rawParser, function(req, res) {
zlib.gunzip(req.body, function(err, buf) {
if(err){
console.log("err:", err );
} else{
console.log("in the inflate callback:",
buf,
"to string:", buf.toString("utf8") );
}
});
res.status(200).send("I'm in ur zipped route");
});
这里有一个要点,包括更详细的日志记录。这个版本也没有任何内置的安全或检查功能。