2012-01-02 13 views
10

Non riesco a capire come eseguire una richiesta PUT HTTP con dati binari verbatim in Python 2.7 con le librerie Python standard.python: HTTP PUT con dati binari non codificati

ho pensato che avrei potuto farlo con urllib2, ma that fails perché urllib2.Request aspetta che i suoi dati in formato application/x-www-form-urlencoded. Non voglio per codificare i dati binari, voglio solo trasmettere parola per parola, dopo le intestazioni che includono

Content-Type: application/octet-stream 
Content-Length: (whatever my binary data length is) 

Questo sembra così semplice, ma io continuo a girare a vuoto e non riesco a capire Come.

Come posso fare questo? (a parte aprire una presa binaria grezza e scriverci sopra)

risposta

12

ho scoperto il mio problema. Sembra che ci sia qualche comportamento oscuro urllib2.Request/urllib2.urlopen() (almeno in Python 2.7)

Il costruttore urllib2.Request(url, data, headers) sembra aspettarsi lo stesso tipo di corda nei suoi parametri URL e dati.

Stavo dando i dati grezzi dei parametri di dati da una chiamata di file read() (che in Python 2.7 lo restituisce sotto forma di una stringa "plain"), ma il mio url era accidentalmente Unicode perché concatenavo una parte dell'URL da il risultato di un'altra funzione che ha restituito le stringhe Unicode.

Piuttosto che cercare di "downcast" url da Unicode -> stringhe semplici, ha tentato di "aggiornare" il parametro data in Unicode e mi ha restituito un errore di codec. (Stranamente, questo accade sulla chiamata urllib2.urlopen() funzione, non il costruttore urllib2.Request)

Quando ho cambiato la mia chiamata di funzione per

# headers contains `{'Content-Type': 'application/octet-stream'}` 
r = urllib2.Request(url.encode('utf-8'), data, headers) 

ha funzionato bene.

+0

Mi sono imbattuto nella [biblioteca delle richieste] (https://github.com/kennethreitz/requests) proprio oggi. In futuro, potresti volerlo usare. –

+2

Ho avuto questo problema esatto, fantastico, amico! –

4

Avete preso in considerazione/provato usando httplib?

HTTPConnection.request (metodo, url [, [corpo, intestazioni]])

This will send a request to the server using the HTTP request method method and the selector url. If the body argument is present, it should be a string of data to send after the headers are finished. Alternatively, it may be an open file object, in which case the contents of the file is sent; this file object should support fileno() and read() methods. The header Content-Length is automatically set to the correct value. The headers argument should be a mapping of extra HTTP headers to send with the request.

+0

"Hai considerato/provato l'uso di httplib?" -- sì. Non funzionerà, si diffonde su dati non ascii. –

+1

Hmmm ...apri l'oggetto file, bene che potrebbe essere d'aiuto. –

+0

@JasonS Ok, solo il check-in –

9

Stai fraintendendo la documentazione: urllib2.Request aspetta i dati già codificati, e per POST che di solito indica il formato application/x-www-form-urlencoded. Siete liberi di associare qualsiasi altro, dati binari, in questo modo:

import urllib2 

data = b'binary-data' 
r = urllib2.Request('http://example.net/put', data, 
        {'Content-Type': 'application/octet-stream'}) 
r.get_method = lambda: 'PUT' 
urllib2.urlopen(r) 

Questo produrrà la richiesta che si desidera:

PUT /put HTTP/1.1 
Accept-Encoding: identity 
Content-Length: 11 
Host: example.net 
Content-Type: application/octet-stream 
Connection: close 
User-Agent: Python-urllib/2.7 

binary-data 
+1

Ma quello che ottengo è 'UnicodeDecodeError: il codec 'ascii' non può decodificare il byte 0xc2 in posizione 0: ordinale non nel range (128)' –

+0

btw, come hai ottenuto la richiesta grezza inviata da urllib2? –

+0

@JasonS Questo perché i tuoi dati sono una * stringa * (un oggetto 'unicode'), non un oggetto' bytes'. Codifica la codifica corretta con ['encode'] (http://docs.python.org/library/stdtypes.html#str.encode). Può essere utile scrivere il programma in Python 3 e quindi eseguire il backport su 2.x. – phihag

1

Questo snipped lavorato per me di mettere un'immagine:

sul sito HTTPS. Se non hai bisogno di HTTPS, usa invece httplib.HTTPConnection (URL).

import httplib 
import ssl 
API_URL="api-mysight.com" 
TOKEN="myDummyToken" 
IMAGE_FILE="myimage.jpg" 
imageID="myImageID" 
URL_PATH_2_USE="/My/image/" + imageID +"?objectId=AAA" 
headers = {"Content-Type":"application/octet-stream", "X-Access-Token": TOKEN} 
imgData = open(IMAGE_FILE, "rb") 
REQUEST="PUT" 
conn = httplib.HTTPSConnection(API_URL, context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)) 
conn.request(REQUEST, URL_PATH_2_USE, imgData, headers) 
response = conn.getresponse() 
result = response.read()