Python Solicita Multipart HTTP POST

Me preguntaba cómo traducir algo como esto usando Python Requests? En urllib2, puede manipular manualmente los datos que se envían a través del cable al servicio API, pero las solicitudes de archivos multipartes de reclamaciones de reclamaciones son fáciles. Sin embargo, al intentar enviar la misma solicitud utilizando la biblioteca de solicitudes, creo que no está especificando algunos parámetros clave en el tipo de contenido para cada una de las dos partes correctamente. ¿Puede alguien por favor arrojar algo de luz sobre este asunto? ¡Gracias de antemano!

def upload_creative(self, account_id, file_path): """""" boundary = '-----------------------------' + str(int(random.random()*1e10)) parts = [] # Set account ID part. parts.append('--' + boundary) parts.append('Content-Disposition: form-data; name="account_id"') parts.append('') parts.append(str(account_id)) # Set creative contents part. parts.append('--' + boundary) parts.append('Content-Disposition: form-data; name="userfile"; filename="%s"' % file_path) parts.append('Content-Type: %s' % mimetypes.guess_type(file_path)[0] or 'application/octet-stream') parts.append('') # TODO: catch errors with opening file. parts.append(open(file_path, 'r').read()) parts.append('--' + boundary + '--') parts.append('') body = '\r\n'.join(parts) headers = {'content-type': 'multipart/form-data; boundary=' + boundary} url = self._resolve_url('/a/creative/uploadcreative') req = urllib2.Request(url, headers=headers, data=body) res = urllib2.urlopen(req) return json.loads(res.read()) 

Cuando examino Firebug desde la interfaz de usuario, obtengo lo siguiente en la fuente POST.

 -----------------------------662549079759661058833120391 Content-Disposition: form-data; name="userfile"; filename="IMG_1377.jpg" Content-Type: image/jpeg ÿØÿáÃExif  MM *            ª        °                     º       Â(       1       Ê2       Ú<       î       i       þ%      p  Apple iPhone 4   H      H     QuickTime 7.7.1 2012:08:17 11:47:11 Mac OS X 10.7.4               "       '     P       0220      (      

ÞAõúu¥}lIf÷û^Â)#´y^)Ô"/·v>n~4ººµ¬æ}FURì·Î 3¿Ãèh»ÐµÈÿ ·|Gu:ß²Ù¤R#+ Á òBôR;ú²¾)!àËn!û:^,E,>^ýêßwû+Ópæ»?i÷û5kéá¹^ 6Ddq°öÁ¯Rù¨¦yãjòÿÙ -----------------------------662549079759661058833120391 Content-Disposition: form-data; name="account_id" 69574 -----------------------------662549079759661058833120391--

Los encabezados en firebug son los siguientes:

 Request Headersview source Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Encoding gzip, deflate Accept-Language en-us,en;q=0.5 Cache-Control no-cache Connection keep-alive Content-Length 1713991 Content-Type multipart/form-data; boundary=---------------------------662549079759661058833120391 Cookie instance_defaults=%7C%20%7Cen_US; access_token=75c48e Host ui.host.com Pragma no-cache Referer http://ui.host.com/ User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:14.0) Gecko/20100101 Firefox/14.0.1 

Supongo que mi pregunta es si hay alguna forma a través de la biblioteca de solicitudes para ajustar los datos de modo que:

 Content-Disposition: form-data; name="userfile"; filename="IMG_1377.jpg" Content-Type: image/jpeg 

y el

 Content-Disposition: form-data; name="account_id" 69574 

Las dos declaraciones están presentes. Siento que tendría que hacer algo como que los archivos sean un diccionario de

 files = {'file': open('image.jpg', 'rb'), 'account_id': 12345} 

pero de alguna manera edita los metadatos de Disposición de contenido de cada una de estas partes por separado

Con las requests , creo que no tienes que ser tan manual, simplemente:

 import requests # ... url = self._resolve_url('/a/creative/uploadcreative') files = {'file': ('userfile', open(filepath, 'rb'))} data = {'account_id': account_id} headers = {'content-type': 'multipart/form-data'} res = requests.post(url, files=files, data=data, headers=headers) return res.json 

Supongo que tu preocupación recae en tu:

 parts.append('Content-Type: %s' % mimetypes.guess_type(file_path)[0] or 'application/octet-stream') 

No me lo he probado más allá de la sombra de la duda. Pero, creo que está incorporado a las solicitudes aquí .

Editar: Parece que puedes tener los campos normales en los archivos dict, como propones:

 files = {'file': open('image.jpg', 'rb'), 'account_id': 12345} 

y podría nombrar el nombre de archivo como desee:

 files = {'file': ('userfile', open('image.jpg', 'rb')), 'account_id': 12345} 

pero, obtendría un body.write(b'Content-Type: text/plain\r\n\r\n') en el campo account_id que probablemente no sea lo que desea y no tiene forma de personalizar la Disposición del contenido para cada campo (aún no estoy seguro de por qué lo necesitarías); tanto para el archivo como para el campo obtendrá: Content-Disposition: form-data – que es lo que muestra para ambos.

No estoy seguro de que pueda hacer exactamente lo que quiere con las requests , tal vez debería intentar una solicitud de función.

  import requests import urllib def upload_creative(self, account_id, file_path): files = [('userfile', (file_path, open(file_path, 'rb'), "image/jpeg" ))] url = self._resolve_url('/a/creative/uploadcreative') url = url + "?" + urlib.urlencode(account_id=account_id) reuests.post(url, files=files) 

Descubrí que en la biblioteca de solicitudes de python (v.0.13.3), sus datos se borrarán si incluye el campo “datos” antes del campo “archivos” en la llamada de solicitud.

Por ejemplo,

 requests.post(url, headers=headers, data=data, files=files) 

producirá datos de formulario vacíos. Sin embargo, lo siguiente enviará el diccionario de datos como datos de formulario.

 requests.post(url, headers=headers, files=files, data=data) 

Gracias a todos por sus respuestas