2013-01-16 46 views
27

मैं रेडमाइन के साथ इंटरफेस के लिए कुछ कोड लिख रहा हूं और मुझे प्रक्रिया के हिस्से के रूप में कुछ फाइलें अपलोड करने की आवश्यकता है, लेकिन मुझे यकीन नहीं है कि एक बाइनरी फ़ाइल युक्त पायथन से POST अनुरोध कैसे करें।पायथन पोस्ट बाइनरी डेटा

मैं आदेशों here की नकल करने की कोशिश कर रहा हूँ:

curl --data-binary "@image.png" -H "Content-Type: application/octet-stream" -X POST -u login:password http://redmine/uploads.xml 

अजगर (नीचे) में, लेकिन यह काम करने के लिए प्रतीत नहीं होता। मुझे यकीन नहीं है कि समस्या किसी फ़ाइल से एन्कोडिंग से संबंधित है या हेडर के साथ कुछ गलत है या नहीं।

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) 

उत्तर

35

मूल रूप से आप जो करते हैं वह सही है। आपके द्वारा लिंक किए गए रेडमाइन दस्तावेज़ों को देखते हुए, ऐसा लगता है कि यूआरएल में डॉट के बाद प्रत्यय पोस्ट डेटा (जेएसओएन के लिए जेएसओएन, एक्सएमएल के लिए .xml) का संकेत देता है, जो आपको प्राप्त प्रतिक्रिया से सहमत है - Processing by AttachmentsController#upload as XML। मुझे लगता है कि दस्तावेज़ों में एक बग है और बाइनरी डेटा पोस्ट करने के लिए आपको http://redmine/uploads.xml के बजाय http://redmine/uploads यूआरएल का उपयोग करने का प्रयास करना चाहिए।

बीटीडब्ल्यू, मैं अत्यधिक पाइथन में http के लिए बहुत अच्छी और बहुत लोकप्रिय Requests लाइब्रेरी की अनुशंसा करता हूं। यह मानक lib (urllib2) में क्या है उससे कहीं बेहतर है। यह प्रमाणीकरण का भी समर्थन करता है लेकिन मैंने इसे यहां अल्पसंख्यक के लिए छोड़ दिया।

import requests 

data = open('./x.png', 'rb').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 के साथ हम क्या भेजा जा रहा है में अंतर की जांच करने के लिए है। इस मैं HTTP प्रॉक्सी (फ़िडलर) पर ट्रैफ़िक भेज रहा हूँ बंदरगाह 8888 पर चल रहा है देखने के लिए:

का उपयोग करते हुए अनुरोध

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 द्वारा भेजे जा रहे हैं और देखें कि यह कब काम करना बंद कर देता है।

+0

कोई विचार क्यों नहीं, लेकिन अनुरोध मॉड्यूल का उपयोग कर सटीक एक ही कोड ठीक काम करता है ... बहुत बहुत धन्यवाद। हालांकि, अब मैं जानना बहुत उत्सुक हूं कि urllib क्यों काम नहीं करता है ... – Mac

0

आप सामग्री-विन्यास हैडर जोड़ने की जरूरत है, इस तरह ख़ाली (हालांकि मैं आधुनिक-अजगर यहां इस्तेमाल किया है, लेकिन सिद्धांत एक जैसा होना चाहिए):

request.headers_out['Content-Disposition'] = 'attachment; filename=%s' % myfname 
+0

कर्ल पूर्ण उदाहरण देख सकते हैं कि, क्यों अजगर करता है की जरूरत नहीं है? – Mac

+0

मुझे लगता है कि कर्ल चुपचाप कर रहा है, हालांकि मैं इस पर खेत नहीं लगाऊंगा - आपका व्यावहारिक विकल्प वायरशर्क का उपयोग करना है और बस देखें कि कर्ल और सर्वर के बीच तार पर क्या चल रहा है (हालांकि स्थानीयहोस्ट पर वायरशर्क का उपयोग करना आसान नहीं है, इसके लिए आपको अलग मशीन रखना होगा)। – mrkafk

+0

स्पष्ट रूप से नहीं: http://pastie.org/private/w4qrctt9t8rx2dzkrg319g#5,15 – Mac

2

इसका कोई विकृत अपलोड करने के लिए कुछ भी नहीं है। HTTP त्रुटि स्पष्ट रूप से 401 अनधिकृत निर्दिष्ट करती है, और आपको बताती है कि सीएसआरएफ टोकन अमान्य है। अपलोड के साथ एक वैध सीएसआरएफ टोकन भेजने का प्रयास करें।

CSRF के बारे में अधिक यहाँ टोकन:

What is a CSRF token ? What is its importance and how does it work?

0

आप unirest उपयोग कर सकते हैं, यह आसान विधि अनुरोध पोस्ट करने के लिए प्रदान करता है। `

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

`

आपको कम से http://stackandqueue.com/?p=57