2015-04-21 17 views
8

Moim celem jest wykonanie części pliku za pomocą żądań i przesłanie pliku (tj. Nie załadowanie go do pamięci, a następnie wykonanie PUT).wnioski - jak przesyłać strumieniowo - częściowy plik

This page wyjaśnia, w jaki sposób to zrobić dla całego pliku:

Wnioski obsługuje strumieniowe przesyłanie, które pozwalają na wysyłanie dużych plików lub strumieni bez czytania ich w pamięci. Aby strumieniowo przesyłać i , wystarczy dostarczyć przedmiot plikopodobny dla ciała:

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

Jednak w moim przypadku chcę wysłać tylko jeden fragment pliku. Czy istnieje sposób, aby to osiągnąć?

W koncepcji, coś jak:

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

hmm, mógłbyś prawdopodobnie napisać generator, który udawałby obiekt podobny do pliku, który odczyta fragment za kulisami, może być trudny, ponieważ nie jestem pewien, co wywołuje żądanie w pliku, ale wydaje się możliwy, jeśli nie ma lepszego rozwiązania – user3012759

+0

@ user3012759: Próbowałem wyszukać to, czego wymaga żądanie w obiekcie podobnym do pliku, bezskutecznie. Należy jednak pamiętać, że akceptuje prosty generator dla [zgłoszeń zakodowanych] (http://docs.python-requests.org/en/latest/user/advanced/#chunk-encoded-requests). –

+0

Myślę, że możesz eksperymentować używając podstawowej klasy podobnej do pliku, która ma metody "czytaj" i "zamknij", a jeśli to nie zadziała, dodawaj metody do swojej klasy, dopóki Żądania przestaną narzekać. :) –

Odpowiedz

1

Ja tylko rzucanie 2 inne odpowiedzi razem więc pokrywa się ze mną, jeśli to nie działa po wyjęciu z pudełka - nie mam możliwości przetestowania tego:

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

Pamiętaj, że Greg nie ** nie ** chce przesłać cały plik. Może powinieneś zmienić 'chunk_size' na coś innego, np.' Blocksize', ponieważ Greg używa "chunksize" do określenia całkowitego rozmiaru danych do przesłania. FWIW, twój kod może być łatwo zmodyfikowany, by wyrwać się z pętli po wysłaniu bajtów "chunksize", jedyną sztuczką jest to, że ostatni blok może być krótki, jeśli 'chunksize% blocksize' nie jest zero. –

+0

Zaktualizowałem przykład, aby określić maksymalną liczbę kawałków – Joe

+0

Ok. To działa. Osoba wywołująca musi upewnić się, że 'blokize' jest dzielnikiem' chunksize', ale mam nadzieję, że to nie jest poważny problem. –

3

Opierając się odpowiedzi Grega na moje pytania myślę dodaje działa najlepiej:

Najpierw trzeba coś owinąć otwarty plik tak, że ogranicza ile danych można odczytać:

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 

ten powinien z grubsza pracować jako dobry obiekt opakowania. Wtedy można go używać tak:

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'}) 

Nagłówki są oczywiście opcjonalne, ale podczas przesyłania strumieniowego danych do serwera, to dobry pomysł, aby być taktowny użytkownika i poinformować serwer jaki rodzaj treści jest to, że właśnie wysyłasz.

+1

dzięki, to jest to, czego potrzebowałem! jeden drobny błąd do "odczytu", gdy "kwota" wynosi -1: 'dane = self.file_obj.read (liczba_adresów, jeśli kwota <0 else min (kwota, pozostała_wartość))' – ryan

Powiązane problemy