Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/rest/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python POST二进制数据_Python_Rest_Post_Urllib2_Redmine - Fatal编程技术网

Python POST二进制数据

Python POST二进制数据,python,rest,post,urllib2,redmine,Python,Rest,Post,Urllib2,Redmine,我正在编写一些与redmine接口的代码,并且我需要上传一些文件作为过程的一部分,但是我不确定如何从包含二进制文件的python发出POST请求 我试图模仿命令: 在python中(如下),但它似乎不起作用。我不确定这个问题是否与文件编码有关,或者标题是否有问题 import urllib2, os FilePath = "C:\somefolder\somefile.7z" FileData = open(FilePath, "rb") length = os.path.getsize(Fi

我正在编写一些与redmine接口的代码,并且我需要上传一些文件作为过程的一部分,但是我不确定如何从包含二进制文件的python发出POST请求

我试图模仿命令:

在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)
。。。
UTF-8中的字节序列无效
行:1
职位:624
最后80个未使用的字符:

7z¼′ÐÐÐз2^212;ë4g¸R您需要像这样添加内容处置头,smth(虽然我在这里使用了mod python,但原理应该是相同的):


基本上你所做的是正确的。查看您链接到的redmine文档,url中点后的后缀似乎表示发布数据的类型(.json表示json,.xml表示xml),这与您得到的响应一致-
由AttachmentsController处理#上传为xml
。我猜文档中可能有一个bug,要发布二进制数据,您应该尝试使用
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服务器检查
用户代理
头并根据其值改变行为并不罕见。尝试逐个更改请求发送的标题,使其与urllib2发送的标题相同,并查看其何时停止工作。

这与格式错误的上载无关。HTTP错误明确指定401未经授权,并告诉您CSRF令牌无效。尝试在上载时发送有效的CSRF令牌

有关csrf令牌的更多信息,请参见:

您可以使用,它提供了发布请求的简单方法。 `


curl不需要它,为什么python需要它?我认为curl是在默默地做这件事,尽管我不会打赌农场在这件事上-你的实际选择是使用Wireshark,只需看看curl和服务器之间的线路上运行着什么(不过在localhost上使用Wireshark并不容易,你必须有单独的机器),显然,curl使用urlencoded格式,至少在我的小文件上是这样。这是我没有想到的另一个选择。我只是在上面发现了使用wireshark(你使用了什么?)我敦促你用同样的方法,所有其他跟踪工具解释(读:扭曲)真正发送和接收的内容。在我的情况下,我欠你一个更正-显然是我使用的工具为POST的内容创建了多部分MIME消息:。显然,这就是内容处置的归属。我使用了类似于
curl--data binary“@users.csv”-b cookie.txt-X POST的命令http://myhost/site.py
,wireshark说它是HTTP/POST,所以我认为curl确实使用了POST,但它使用了urlencoded文件,其中包含的数据包内容与我在上面评论中链接的第一个pastie类似。不知道为什么,但是使用requests模块,完全相同的代码可以正常工作。。。谢谢。虽然,现在我很想知道为什么urllib不工作……请看这里:
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
import unirest
 
def callback(response):
 print "code:"+ str(response.code)
 print "******************"
 print "headers:"+ str(response.headers)
 print "******************"
 print "body:"+ str(response.body)
 print "******************"
 print "raw_body:"+ str(response.raw_body)
 
# consume async post request
def consumePOSTRequestASync():
 params = {'test1':'param1','test2':'param2'}
 
 # we need to pass a dummy variable which is open method
 # actually unirest does not provide variable to shift between
 # application-x-www-form-urlencoded and
 # multipart/form-data
  
 params['dummy'] = open('dummy.txt', 'r')
 url = 'http://httpbin.org/post'
 headers = {"Accept": "application/json"}
 # call get service with headers and params
 unirest.post(url, headers = headers,params = params, callback = callback)
 
 
# post async request multipart/form-data
consumePOSTRequestASync()