Python POST二进制数据
我正在编写一些代码来与Redmine交互,并且作为过程的一部分,我需要上传一些文件,但是我不确定如何从包含二进制文件的python发出POST请求。
我正在尝试模仿这里的命令:
curl --data-binary "@image.png" -H "Content-Type: application/octet-stream" -X POST -u login:password http://redmine/uploads.xml
在python中(如下),但它似乎不起作用。我不确定问题是否与编码文件有关,或者标题是否有问题。
import urllib2, os
FilePath = "C:\somefolder\somefile.7z"
FileData = open(FilePath, "rb")
length = os.path.getsize(FilePath)
password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
password_manager.add_password(None, 'http://redmine/', 'admin', 'admin')
auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
opener = urllib2.build_opener(auth_handler)
urllib2.install_opener(opener)
request = urllib2.Request( r'http://redmine/uploads.xml', FileData)
request.add_header('Content-Length', '%d' % length)
request.add_header('Content-Type', 'application/octet-stream')
try:
response = urllib2.urlopen( request)
print response.read()
except urllib2.HTTPError as e:
error_message = e.read()
print error_message
我可以访问服务器,它看起来像是编码错误:
...
invalid byte sequence in UTF-8
Line: 1
Position: 624
Last 80 unconsumed characters:
7z¼¯'ÅÐз2^Ôøë4g¸R<süðí6kĤª¶!»=}jcdjSPúá-º#»ÄAtD»H7Ê!æ½]j):
(further down)
Started POST "/uploads.xml" for 192.168.0.117 at 2013-01-16 09:57:49 -0800
Processing by AttachmentsController#upload as XML
WARNING: Can't verify CSRF token authenticity
Current user: anonymous
Filter chain halted as :authorize_global rendered or redirected
Completed 401 Unauthorized in 13ms (ActiveRecord: 3.1ms)
-
基本上您所做的是正确的。查看链接到的redmine文档,URL中的点后的后缀似乎表示发布数据的类型(JSON为.json,XML为.xml),这与您获得的响应-
一致Processing by AttachmentsController#upload as XML
。我猜可能是文档中存在一个错误,要发布二进制数据,您应该尝试使用http://redmine/uploads
url而不是http://redmine/uploads.xml
。顺便说一句,我强烈建议在Python中使用非常好的和非常受欢迎的HTTP请求库。它比标准库(urllib2)中的库要好得多。它也支持身份验证,但是为了简洁起见,我跳过了它。
import requests with open('./x.png', 'rb') as f: data = f.read() res = requests.post(url='http://httpbin.org/post', data=data, headers={'Content-Type': 'application/octet-stream'}) # let's check if what we sent is what we intended to send... import json import base64 assert base64.b64decode(res.json()['data'][len('data:application/octet-stream;base64,'):]) == data
更新
为了弄清楚为什么它适用于请求而不适用于urllib2,我们必须检查发送内容的差异。要查看此信息,我将流量发送到端口8888上运行的http代理(Fiddler):
使用请求
import requests data = 'test data' res = requests.post(url='http://localhost:8888', data=data, headers={'Content-Type': 'application/octet-stream'})
我们看
POST http://localhost:8888/ HTTP/1.1 Host: localhost:8888 Content-Length: 9 Content-Type: application/octet-stream Accept-Encoding: gzip, deflate, compress Accept: */* User-Agent: python-requests/1.0.4 CPython/2.7.3 Windows/Vista test data
并使用urllib2
import urllib2 data = 'test data' req = urllib2.Request('http://localhost:8888', data) req.add_header('Content-Length', '%d' % len(data)) req.add_header('Content-Type', 'application/octet-stream') res = urllib2.urlopen(req)
我们得到
POST http://localhost:8888/ HTTP/1.1 Accept-Encoding: identity Content-Length: 9 Host: localhost:8888 Content-Type: application/octet-stream Connection: close User-Agent: Python-urllib/2.7 test data
我看不出有任何差异可以保证您观察到不同的行为。话虽如此,http服务器检查
User- Agent
标头并根据其值改变行为的情况并不少见。尝试一个接一个地更改请求发送的标头,使其与urllib2发送的标头相同,然后查看何时停止工作。