5

我正在尝试用 Python 和 requests 库替换 curl。使用 curl,我可以使用 curl -T 选项将单个 XML 文件上传到 REST 服务器。我一直无法对请求库做同样的事情。

一个基本的场景有效:

payload = '<person test="10"><first>Carl</first><last>Sagan</last></person>'
headers = {'content-type': 'application/xml'}
r = requests.put(url, data=payload, headers=headers, auth=HTTPDigestAuth("*", "*"))

当我通过打开 XML 文件将有效负载更改为更大的字符串时,.put 方法会挂起(我使用编解码器库来获取正确的 unicode 字符串)。例如,对于 66KB 的文件:

xmlfile = codecs.open('trb-1996-219.xml', 'r', 'utf-8')
headers = {'content-type': 'application/xml'}
content = xmlfile.read()
r = requests.put(url, data=content, headers=headers, auth=HTTPDigestAuth("*", "*"))

我一直在研究使用多部分选项(文件),但服务器似乎不喜欢这样。

所以我想知道是否有一种方法可以在 Python 请求库中模拟 curl -T 行为。

更新 1:程序在 textmate 中挂起,但在命令行上引发 UnicodeEncodeError 错误。看来这一定是问题所在。所以问题是:有没有办法使用请求库将 unicode 字符串发送到服务器?

更新 2:感谢 Martijn Pieters 的评论,UnicodeEncodeError 消失了,但出现了一个新问题。对于文字 (ASCII) XML 字符串,日志记录显示以下行:

2012-11-11 15:55:05,154 INFO Starting new HTTP connection (1): my.ip.address
2012-11-11 15:55:05,294 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 401 211
2012-11-11 15:55:05,430 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 201 0

似乎服务器总是退回第一次身份验证尝试(?),但随后接受第二次。

将文件对象 (open('trb-1996-219.xml', 'rb')) 传递给数据后,日志文件显示:

2012-11-11 15:50:54,309 INFO Starting new HTTP connection (1): my.ip.address
2012-11-11 15:50:55,105 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 401 211
2012-11-11 15:51:25,603 WARNING Retrying (0 attempts remain) after connection broken by 'BadStatusLine("''",)': /v1/documents?uri=/example/test.xml

因此,第一次尝试像以前一样被阻止,但没有进行第二次尝试。

根据 Martijn Pieters(下)的说法,第二个问题可以用有故障的服务器(空行)来解释。我会调查这个,但如果有人有解决方法(除了使用 curl)我不介意听到它。

我仍然对请求库对于小字符串和文件对象的行为如此不同感到惊讶。文件对象在到达服务器之前不是序列化了吗?

4

3 回答 3

9

要 PUT 大文件,请不要将它们读入内存。只需将文件作为data关键字传递:

xmlfile = open('trb-1996-219.xml', 'rb')
headers = {'content-type': 'application/xml'}
r = requests.put(url, data=xmlfile, headers=headers, auth=HTTPDigestAuth("*", "*"))

此外,您以 unicode 格式打开文件(从 UTF-8 解码)。当您将其发送到远程服务器时,您需要原始字节,而不是 unicode 值,并且您应该将文件作为二进制文件打开。

于 2012-11-11T14:01:11.637 回答
1

摘要式身份验证始终要求您向服务器发出至少两个请求。第一个请求不包含任何身份验证数据。第一个请求将失败,并返回 401“需要授权”响应代码和用于散列密码等的摘要质询(称为 nounce)(确切的细节在这里无关紧要)。这用于向服务器发出第二个请求,其中包含您的凭据与质询进行哈希处理。

问题在于这两个步骤的身份验证:您的大文件已经与第一个未经授权的请求一起发送(发送无效),但在第二个请求中,文件对象已经在 EOF 位置。由于文件大小也在第二个请求的 Content-length 标头中发送,这会导致服务器等待永远不会发送的文件。

您可以使用 requests Session 解决它,并首先发出一个简单的身份验证请求(例如 GET 请求)。然后使用与第一个请求相同的摘要质询发出包含实际有效负载的第二个 PUT 请求。

sess = requests.Session()
sess.auth = HTTPDigestAuth("*", "*")
sess.get(url)
headers = {'content-type': 'application/xml'}
with codecs.open('trb-1996-219.xml', 'r', 'utf-8') as xmlfile:
    sess.put(url, data=xmlfile, headers=headers)
于 2013-05-17T08:14:11.607 回答
0

我在 python 中使用请求使用命令上传 XML 文件。首先打开文件使用 open() file = open("PIR.xsd") fragment = file.read() file.close() 将 XML 文件的数据复制到请求的有效负载中并发布 payload = {'key':'PFAkrzjmuZR957','xmlFragment':fragment} r = requests.post(URL,data=payload) 以检查 html 验证码 print (r.text)

于 2018-07-24T11:09:08.403 回答