2012-01-02 14 views
10

No puedo entender cómo realizar una solicitud HTTP PUT con datos binarios literales en Python 2.7 con las bibliotecas estándar de Python.python: HTTP PUT con datos binarios sin codificar

Pensé que podría hacerlo con urllib2, pero that fails porque urllib2.Request espera sus datos en el formato application/x-www-form-urlencoded. No quiero para codificar los datos binarios, sólo quiero transmitirlo pie de la letra, después de las cabeceras que incluyen

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

Esto parece muy sencillo, pero sigo dando vueltas en círculos y parece que no puede averiguar cómo.

¿Cómo puedo hacer esto? (aparte de abrir un zócalo binario sin formato y escribir en él)

Respuesta

12

descubrí que mi problema. Parece que hay algún comportamiento oscuro en urllib2.Request/urllib2.urlopen() (al menos en Python 2.7)

El constructor urllib2.Request(url, data, headers) parece esperar el mismo tipo de cadena en sus parámetros de URL y de datos.

Estaba dando los datos del parámetro de datos sin procesar desde un archivo read() llamada (que en Python 2.7 lo devuelve en forma de una cadena 'simple'), pero mi url fue accidentalmente Unicode porque concatené una parte de la URL de el resultado de otra función que devolvió cadenas Unicode.

En lugar de intentar "bajar" url desde Unicode -> cadenas simples, intentó "subir" el parámetro data a Unicode, y me dio un error de códec. (Por extraño que parezca, esto sucede en la llamada urllib2.urlopen() función, no el constructor urllib2.Request)

Cuando cambié mi llamada a la función

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

funcionó bien.

+0

Me topé con [la biblioteca de solicitudes] (https://github.com/kennethreitz/requests) solo hoy. En el futuro, es posible que desee usar eso. –

+2

Tuve este problema exacto, increíble, ¡salud! –

4

¿Ha considerado/intentado utilizar httplib?

HTTPConnection.request (método, url [, [cuerpo, encabezados]])

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

"¿Ha considerado o ha intentado usar httplib?" - si. No funcionará, vomita en datos que no son ascii. –

+1

Hmmm ...abrir el objeto de archivo, bueno, eso podría ser de ayuda. –

+0

@JasonS Ok, solo registrándome –

9

Usted está leyendo mal la documentación: urllib2.Request espera que los datos ya codificados y POST que por lo general significa el formato application/x-www-form-urlencoded. Usted es libre de asociar cualquier otro, datos binarios, como esto:

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) 

Esto producirá la solicitud que desee:

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

Pero lo que obtengo es 'UnicodeDecodeError: 'ascii' el códec no puede decodificar el byte 0xc2 en la posición 0: ordinal no está dentro del rango (128)' –

+0

Por cierto, ¿cómo obtuviste la solicitud sin procesar enviada desde urllib2? –

+0

@JasonS Eso se debe a que sus datos son una * cadena * (un objeto 'unicode'), no un objeto' bytes'. Codifíquelo con la codificación correcta con ['encode'] (http://docs.python.org/library/stdtypes.html#str.encode). Puede ser útil escribir el programa en Python 3 y luego respaldarlo en 2.x. – phihag

1

Esta cortó trabajaron para mí poner una imagen:

en el sitio HTTPS. Si no necesita HTTPS, use httplib.HTTPConnection (URL) en su lugar.

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