2012-09-12 18 views
104

Come inviare un multipart/form-data con richieste in python? Come inviare un file, ho capito, ma come inviare i dati del modulo con questo metodo non può capire.Come inviare un "multipart/form-data" con richieste in python?

+0

la tua domanda non è chiara. Cosa vuoi ottenere? Desideri inviare "multipart/form-data" senza un file upload nel modulo? –

+2

Il fatto che il parametro 'files' sia usato per fare entrambe le cose è un'API pessima. Ho sollevato il problema intitolato [Invio di dati multipart - abbiamo bisogno di API migliori] (https://github.com/kennethreitz/requests/issues/935) per risolvere questo problema. Se accetti che l'uso del parametro 'files' per inviare dati mulitpart è fuorviante al massimo ti preghiamo di chiedere di cambiare l'API nel problema precedente. –

+0

@PiotrDobrogost il problema è stato chiuso. Non incoraggiare le persone a commentare questioni chiuse, pertinenti o meno. –

risposta

81

In sostanza, se si specifica un parametro files (un dizionario), quindi requests invierà un POST multipart/form-data invece di un POST application/x-www-form-urlencoded. Non si sono limitati a utilizzare file effettivi in ​​quel dizionario, però:

>>> import requests 
>>> response = requests.post('http://httpbin.org/post', files=dict(foo='bar')) 
>>> response.status_code 
200 

e httpbin.org ti permette di sapere che cosa gli header che hai postato con; in response.json() abbiamo:

>>> from pprint import pprint 
>>> pprint(response.json()['headers']) 
{u'Accept': u'*/*', 
u'Accept-Encoding': u'gzip, deflate, compress', 
u'Connection': u'close', 
u'Content-Length': u'141', 
u'Content-Type': u'multipart/form-data; boundary=33b4531a79be4b278de5f5688fab7701', 
u'Host': u'httpbin.org', 
u'User-Agent': u'python-requests/2.2.1 CPython/2.7.6 Darwin/13.2.0', 
u'X-Request-Id': u'eaf6baf8-fc3d-456b-b17d-e8219ccef1b1'} 

files può essere anche una lista di tuple a due valori, se avete bisogno di ordinare e/o campi multipli con lo stesso nome:

requests.post('http://requestb.in/xucj9exu', files=(('foo', 'bar'), ('spam', 'eggs'))) 

Se si specifica sia files e data, quindi dipende dal valore di data che verrà utilizzato per creare il corpo POST. Se data è una stringa, verrà utilizzata solo; altrimenti vengono utilizzati sia data e files, con gli elementi in data elencati per primi.

+4

Codificherà qualsiasi cosa inviata a 'file' come parametro di un file reale nella codifica multipart. Questo non creerà un modulo rigoroso ma piuttosto un modulo con tutti i parametri del file. Vedi [questo] (https://github.com/kennethreitz/requests/issues/1081) come riferimento. –

+0

@ sigmavirus24: l'API delle richieste si è evoluta da quando l'ho pubblicata; fammi indagare se questo ha bisogno di un aggiornamento ora. In ogni caso, questo angolo dell'API [ha avuto bisogno di una revisione per un po 'ora] (https://github.com/kennethreitz/requests/issues/935). –

+0

scuse. StackOverflow lo mette nelle prime posizioni e dimentico che riorganizzare le domande e devo dare un'occhiata alle date delle risposte/delle domande. –

68

Poiché le risposte precedenti sono state scritte, le richieste sono cambiate. Dai un'occhiata allo bug thread at Github per maggiori dettagli e allo this comment per un esempio.

In breve, il parametro file prende uno dict con la chiave come nome del campo modulo e il valore è una stringa o una tupla di 2, 3 o 4 lunghezze, come descritto nella sezione POST a Multipart-Encoded File nelle richieste quickstart:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})} 

in quanto precede, la tupla è composto come segue:

(filename, data, content_type, headers) 

Se il valore è solo una stringa, il nome del file sarà la stessa come la chiave, come il seguente:

>>> files = {'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'} 

Content-Disposition: form-data; name="obvius_session_id"; filename="obvius_session_id" 
Content-Type: application/octet-stream 

72c2b6f406cdabd578c5fd7598557c52 

Se il valore è una tupla e la prima voce è None la proprietà nome del file non sarà incluso:

>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')} 

Content-Disposition: form-data; name="obvius_session_id" 
Content-Type: application/octet-stream 

72c2b6f406cdabd578c5fd7598557c52 
+1

Cosa fare se è necessario distinguere il 'nome' e' nomefile', ma hanno anche più campi con lo stesso nome? – Michael

+1

Ho un problema simile a quello di @Michael. Puoi dare un'occhiata alla domanda e suggerire qualcosa? [collegamento] (http://stackoverflow.com/questions/30683352/how-to-upload-multipart-encode-file-and-form-data-as-a-payload) – Shaardool

+0

qualcuno ha risolto questo problema avendo più campi con lo stesso nome? – user3131037

27

è necessario utilizzare il parametro files per inviare una richiesta di modulo POST multipart anche quando si non è necessario caricare alcun file.

Dall'originale requests fonte:

def request(method, url, **kwargs): 
    """Constructs and sends a :class:`Request <Request>`. 

    ... 
    :param files: (optional) Dictionary of ``'name': file-like-objects`` 
     (or ``{'name': file-tuple}``) for multipart encoding upload. 
     ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 
     3-tuple ``('filename', fileobj, 'content_type')`` 
     or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, 
     where ``'content-type'`` is a string 
     defining the content type of the given file 
     and ``custom_headers`` a dict-like object 
     containing additional headers to add for the file. 

Il più semplice modulo di richiesta multipart che include sia i file da caricare e campi del modulo sarà simile a questo:

multipart_form_data = { 
    'file1': open('myfile.zip', 'rb'), 
    'file2': ('custom_file_name.zip', open('myfile.zip', 'rb')), 
    'action': ('', 'store'), 
    'path': ('', '/path1') 
} 

response = requests.post('https://httpbin.org/post', files=multipart_form_data) 

print(response.content) 

nota la stringa vuota come il primo elemento nella tupla per i campi di testo semplice - questo è un segnaposto per il campo filename che viene utilizzato solo per i caricamenti di file, ma per il campo di testo il segnaposto vuoto deve essere presente nell'ordine per i dati da inviare.


Se questa API non è sufficiente divinatorio per voi, o se avete bisogno di inviare più campi con lo stesso nome, quindi considerare l'utilizzo di requests toolbelt ( pip install requests_toolbelt) che è un'estensione del modulo core requests che fornisce il supporto per il file upload streaming oltre allo MultipartEncoder che può essere usato al posto di files e che accetta parametri sia come dizionari che tuple.

MultipartEncoder può essere utilizzato sia per richieste multipart con o senza campi di caricamento effettivi. Deve essere assegnato al parametro data.

import requests 
from requests_toolbelt.multipart.encoder import MultipartEncoder 

multipart_data = MultipartEncoder(
    fields={ 
      # a file upload field 
      'file': ('file.py', open('file.py', 'rb'), 'text/plain') 
      # plain text fields 
      'field0': 'value0', 
      'field1': 'value1', 
      } 
    ) 

response = requests.post('http://httpbin.org/post', data=multipart_data, 
        headers={'Content-Type': multipart_data.content_type}) 

Se avete bisogno di inviare più campi con lo stesso nome, o se l'ordine dei campi modulo è importante, quindi una tupla o una lista può essere utilizzato al posto di un dizionario, vale a dire:

multipart_data = MultipartEncoder(
    fields=(
      ('action', 'store'), 
      ('path', '/path1'), 
      ('path', '/path2'), 
      ('path', '/path3'), 
      ) 
    ) 
+0

Grazie per questo. L'ordine delle chiavi era importante per me e questo ha aiutato molto. – Splendor

+0

Incredibile. Inspiegabilmente, una API con cui sto lavorando richiede 2 valori diversi per la stessa chiave. È incredibile Grazie. – ajon

+0

@ccpizza, cosa significa in realtà questa linea? > "('file.py', aperto ('file.py', 'rb'), 'text/plain')". Non funziona per me :( –