2015-06-05 11 views
7

Próbuję przesyłać strumieniowo plik .gz z S3 przy użyciu boto i iterować po liniach rozpakowanego pliku tekstowego. Tajemniczo pętla nigdy się nie kończy; gdy cały plik zostanie odczytany, iteracja zostanie uruchomiona ponownie na początku pliku.Nieskończona pętla podczas przesyłania strumieniowego pliku .gz z S3 przy użyciu boto

Powiedzmy utworzyć i przesłać plik wejściowy jak następuje:

> echo '{"key": "value"}' > foo.json 
> gzip -9 foo.json 
> aws s3 cp foo.json.gz s3://my-bucket/my-location/ 

i uruchom następujące Pythona skrypt:

import boto 
import gzip 

connection = boto.connect_s3() 
bucket = connection.get_bucket('my-bucket') 
key = bucket.get_key('my-location/foo.json.gz') 
gz_file = gzip.GzipFile(fileobj=key, mode='rb') 
for line in gz_file: 
    print(line) 

wynik jest:

b'{"key": "value"}\n' 
b'{"key": "value"}\n' 
b'{"key": "value"}\n' 
...forever... 

Dlaczego to się dzieje? Myślę, że musi być coś bardzo podstawowego, czego mi brakuje.

Odpowiedz

9

Ah, boto. Problem polega na tym, że metoda read ponownie ładuje klucz, jeśli wywołasz go po pełnym odczytaniu klucza (porównaj odczyt i następne metody, aby zobaczyć różnicę).

To nie jest najczystszym sposobem, aby to zrobić, ale to rozwiązuje problem:

import boto 
import gzip 

class ReadOnce(object): 
    def __init__(self, k): 
     self.key = k 
     self.has_read_once = False 

    def read(self, size=0): 
     if self.has_read_once: 
      return b'' 
     data = self.key.read(size) 
     if not data: 
      self.has_read_once = True 
     return data 

connection = boto.connect_s3() 
bucket = connection.get_bucket('my-bucket') 
key = ReadOnce(bucket.get_key('my-location/foo.json.gz')) 
gz_file = gzip.GzipFile(fileobj=key, mode='rb') 
for line in gz_file: 
    print(line) 
+0

+1: to jest genialne. W rzeczywistości, używając swojego opakowania, mogę odczytać pandas DataFrame bezpośrednio ze skompresowanego obiektu S3. Dzięki! –

+0

Nice! Odnosi się to równie dobrze do pobrań plików CSV, które dla mnie również wykazywały dziwne zachowanie, takie jak koniec pliku, łączący się z początkiem. Chciałbym wiedzieć, dlaczego boto robi rzeczy w ten sposób - bardzo duże wrażenie IMO – killthrush

+0

Warto również zwrócić uwagę, kiedy uruchamiam to rozwiązanie jako menedżer kontekstu, potrzebowałem również funkcji 'close'. Po prostu przekazuje 'close' do' self.key.close() '. – killthrush

0

Dzięki zweiterlinde dla wspaniałej intuicji i excellent answer provided.

Szukałem rozwiązania czytać skompresowany obiekt S3 bezpośrednio do Pandy DataFrame i używając jego opakowanie, może być wyrażona w dwóch liniach:

with gzip.GzipFile(fileobj=ReadOnce(bucket.get_key('my/obj.tsv.gz')), mode='rb') as f: 
    df = pd.read_csv(f, sep='\t') 
+1

Python nie jest o "kto może dostać' to' w najmniejszej ilości linii ", idź czytać Biblię ->' python -m this' –

Powiązane problemy