2012-11-11 12 views
5

Sto provando a sostituire Curl con Python & nella libreria delle richieste. Con il ricciolo, posso caricare un singolo file XML su un server REST con l'opzione curl -T. Non sono stato in grado di fare lo stesso con la libreria delle richieste.Carica un file XML di grandi dimensioni con la libreria Richieste Python

Uno scenario di base funziona:

payload = '<person test="10"><first>Carl</first><last>Sagan</last></person>' 
headers = {'content-type': 'application/xml'} 
r = requests.put(url, data=payload, headers=headers, auth=HTTPDigestAuth("*", "*")) 

quando cambio di carico utile in una stringa più grande con l'apertura di un file XML, il metodo .Put si blocca (io uso la biblioteca codec per ottenere una stringa unicode corretta). Ad esempio, con un file di 66KB:

xmlfile = codecs.open('trb-1996-219.xml', 'r', 'utf-8') 
headers = {'content-type': 'application/xml'} 
content = xmlfile.read() 
r = requests.put(url, data=content, headers=headers, auth=HTTPDigestAuth("*", "*")) 

Ho cercato nella utilizzando l'opzione multipart (file), ma il server non sembra piace che.

Quindi mi chiedevo se c'è un modo per simulare il comportamento di curl -T nella libreria di richieste Python.

UPDATE 1: Il programma si blocca in textmate, ma genera un errore UnicodeEncodeError sulla riga di comando. Sembra che questo sia il problema Quindi la domanda sarebbe: c'è un modo per inviare stringhe unicode ad un server con la libreria delle richieste?

AGGIORNAMENTO 2: Grazie al commento di Martijn Pieters, UnicodeEncodeError è andato via, ma si è verificato un nuovo problema. Con una stringa letterale XML (ASCII), la registrazione mostra le seguenti righe:

2012-11-11 15:55:05,154 INFO Starting new HTTP connection (1): my.ip.address 
2012-11-11 15:55:05,294 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 401 211 
2012-11-11 15:55:05,430 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 201 0 

Sembra server rimbalza sempre il primo tentativo di autenticazione, ma poi accetta la seconda (?).

Con un oggetto file (aperta ('TRB-1996-219.xml', 'rb')) passato ai dati, il file di log mostra:

2012-11-11 15:50:54,309 INFO Starting new HTTP connection (1): my.ip.address 
2012-11-11 15:50:55,105 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 401 211 
2012-11-11 15:51:25,603 WARNING Retrying (0 attempts remain) after connection broken by 'BadStatusLine("''",)': /v1/documents?uri=/example/test.xml 

Quindi, primo tentativo viene bloccato come prima, ma nessun secondo tentativo è fatto.

Secondo Martijn Pieters (sotto), il secondo problema può essere spiegato da un server difettoso (riga vuota). Lo esaminerò, ma se qualcuno ha una soluzione alternativa (a parte l'uso del ricciolo) non mi dispiacerebbe sentirlo.

E sono ancora sorpreso che la libreria delle richieste si comporti in modo così diverso per oggetti di stringa e file di piccole dimensioni. L'oggetto file non è serializzato prima di arrivare comunque al server?

risposta

9

Per PUT di file di grandi dimensioni, non leggerli in memoria. È sufficiente passare il file come la parola data:

xmlfile = open('trb-1996-219.xml', 'rb') 
headers = {'content-type': 'application/xml'} 
r = requests.put(url, data=xmlfile, headers=headers, auth=HTTPDigestAuth("*", "*")) 

Inoltre, si stava aprendo il file come unicode (la decodifica da UTF-8). Come lo invierete a un server remoto, avrete bisogno di byte non elaborati, non di valori Unicode, e dovreste invece aprire il file come binario.

+0

Grazie per la risposta rapida. In questo modo è stato risolto l'errore UnicodeEncodeError, ma è stato introdotto un ConnectionError: MaxRetryError. E il server non è inattivo, perché posso caricare il file con curl. –

+0

@M_breeb: è necessario utilizzare il modulo 'logging' per scoprire perché lo è; i tentativi di connessione al server falliscono per qualche motivo, * prima di * essere in grado di inserire i dati. Il pool di connessione 'urllib3' ripete la connessione, e registrerà l'errore ogni volta che si collegherà al modulo' logging'. –

+0

'logging di importazione', quindi' logging.basicConfig() 'è il metodo di base per ottenere rapidamente quell'output. –

1

L'autenticazione del digest richiede sempre di effettuare almeno due richieste al server. La prima richiesta non contiene dati di autenticazione. Questa prima richiesta fallirà con un codice di risposta 401 "Autorizzazione necessaria" e una sfida digest (chiamata un sostantivo) da utilizzare per l'hashing della password ecc. (I dettagli esatti non contano qui). Questo è usato per fare una seconda richiesta al server contenente le credenziali hash con la sfida.

Il problema è nell'autenticazione in due passaggi: il tuo file di grandi dimensioni era già stato inviato con la prima richiesta non autorizzata (invio invano) ma sulla seconda richiesta l'oggetto file si trova già nella posizione EOF. Poiché anche la dimensione del file è stata inviata nell'intestazione Content-length della seconda richiesta, il server deve attendere un file che non verrà mai inviato.

È possibile risolverlo utilizzando una richiesta Session e innanzitutto effettuare una richiesta semplice per scopi di autenticazione (ad esempio una richiesta GET). Quindi effettuare una seconda richiesta PUT contenente il payload effettivo utilizzando lo stesso challenge challenge della prima richiesta.

sess = requests.Session() 
sess.auth = HTTPDigestAuth("*", "*") 
sess.get(url) 
headers = {'content-type': 'application/xml'} 
with codecs.open('trb-1996-219.xml', 'r', 'utf-8') as xmlfile: 
    sess.put(url, data=xmlfile, headers=headers) 
Problemi correlati