2015-04-21 13 views
8

Il mio obiettivo è fare un PUT di parte di un file utilizzando le richieste e lo streaming del file (ad esempio, non caricarlo in memoria e quindi eseguire il PUT).richieste - come eseguire il caricamento in streaming - file parziale

This page spiega come si dovrebbe farlo per un intero file:

Richieste supporta il caricamento in streaming, che consentono di inviare grandi stream o file senza leggerli nella memoria. Per eseguire lo streaming e caricati, è sufficiente fornire un oggetto simile a file per il vostro corpo:

with open('massive-body', 'rb') as f: 
    requests.post('http://some.url/streamed', data=f) 

Tuttavia nel mio caso voglio inviare un solo pezzo del file. C'è un modo per realizzare questo?

Nel concetto, qualcosa di simile a:

with open('massive-body', 'rb') as f: 
    requests.post('http://some.url/streamed', data=f.read(chunksize)) 
+0

hmm, si potrebbe probabilmente scrivere un generatore che finge di essere un oggetto simile a un file che leggerà un blocco dietro le quinte, può essere difficile anche se non sono sicuro di quali richieste di chiamata fanno su un file, ma sembra possibile se nessuno ha trovato una soluzione migliore – user3012759

+0

@ user3012759: Ho provato a cercare ciò che Richieste richiede in un oggetto simile a file, senza risultato. Tuttavia, si noti che accetta un generatore semplice per [Chunk-Encoded Requests] (http://docs.python-requests.org/en/latest/user/advanced/#chunk-encoded-requests) –

+0

Immagino che tu possa sperimentare utilizzando una classe di tipo file di base che ha metodi 'read' e' close', e se ciò non funziona, continua ad aggiungere metodi alla tua classe fino a quando le richieste smettono di lamentarsi. :) –

risposta

1

sto solo gettando 2 altre risposte insieme in modo portare con me se non funziona out of the box - non ho modo di testare questa:

Lazy Method for Reading Big File in Python?

http://docs.python-requests.org/en/latest/user/advanced/#chunk-encoded-requests

def read_in_chunks(file_object, blocksize=1024, chunks=-1): 
    """Lazy function (generator) to read a file piece by piece. 
    Default chunk size: 1k.""" 
    while chunks: 
     data = file_object.read(blocksize) 
     if not data: 
      break 
     yield data 
     chunks -= 1 

requests.post('http://some.url/chunked', data=read_in_chunks(f)) 
+0

Si noti che Greg ** non ** desidera caricare l'intero file. Forse dovresti cambiare il tuo 'chunk_size' in qualcos'altro, ad es.' Blocksize', dato che Greg sta usando 'chunksize' per indicare la dimensione totale dei dati da trasferire. FWIW, il tuo codice può essere facilmente modificato per uscire dal ciclo quando i byte 'chunksize' sono stati inviati, l'unico trucco è che l'ultimo blocco potrebbe essere breve se' chunksize% blocksize' non è zero. –

+0

Ho aggiornato l'esempio in modo da poter specificare un numero massimo di blocchi – Joe

+0

Ok. Che funzioni. Il chiamante deve assicurarsi che 'blocksize' sia un divisore di' chunksize', ma si spera che non sia un grosso problema. –

3

in base al largo risposte di Greg alle mie domande penso che il seguente funzionerà meglio:

In primo luogo avrete bisogno di qualcosa per avvolgere il file aperto in modo che limita la quantità di dati possono essere letti:

class FileLimiter(object): 
    def __init__(self, file_obj, read_limit): 
     self.read_limit = read_limit 
     self.amount_seen = 0 
     self.file_obj = file_obj 

     # So that requests doesn't try to chunk the upload but will instead stream it: 
     self.len = read_limit 

    def read(self, amount=-1): 
     if self.amount_seen >= self.read_limit: 
      return b'' 
     remaining_amount = self.read_limit - self.amount_seen 
     data = self.file_obj.read(min(amount, remaining_amount)) 
     self.amount_seen += len(data) 
     return data 

Questo dovrebbe funzionare approssimativamente come un buon oggetto wrapper. Quindi si usa in questo modo:

with open('my_large_file', 'rb') as file_obj: 
    file_obj.seek(my_offset) 
    upload = FileLimiter(file_obj, my_chunk_limit) 
    r = requests.post(url, data=upload, headers={'Content-Type': 'application/octet-stream'}) 

Le intestazioni sono ovviamente opzionali, ma quando lo streaming dati a un server, è una buona idea di essere un utente premuroso e dire al server quale il tipo di contenuti è che stai mandando.

+1

grazie, questo è proprio quello di cui avevo bisogno! una correzione di bug minore a 'read' per quando' amount' è -1: 'data = self.file_obj.read (remaining_amount se amount <0 else min (amount, remaining_amount))' – ryan

Problemi correlati