2015-06-05 14 views
7

Sto tentando di eseguire lo streaming di un file .gz da S3 utilizzando boto e iterato sulle righe del file di testo non compresso. Misteriosamente, il ciclo non termina mai; quando l'intero file è stato letto, l'iterazione si riavvia all'inizio del file.Ciclo infinito durante lo streaming di un file .gz da S3 utilizzando boto

Diciamo che ho creare e caricare un file di input come la seguente:

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

e corro il seguente script Python:

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) 

Il risultato è:

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

Perché sta succedendo? Penso che ci debba essere qualcosa di molto essenziale che mi manca.

risposta

9

Ah, boto. Il problema è che il metodo di lettura ridistribuisce la chiave se la chiami dopo che la chiave è stata completamente letta una volta (confronta i metodi read e next per vedere la differenza).

Questo non è il modo più pulito per farlo, ma risolve il problema:

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: questo è brillante. Infatti, usando il tuo wrapper, posso leggere un DataFrame di panda direttamente da un oggetto S3 compresso. Grazie! –

+0

Bello! Questo si applica ugualmente bene ai download CSV, che per me mostravano anche comportamenti bizzarri come la fine del file che si concatenava con l'inizio. Mi piacerebbe sapere perché boto fa le cose in questo modo - molto grande trucco IMO – killthrush

+0

Anche degno di nota quando si esegue questa soluzione come gestore di contesto, avevo bisogno di implementare anche una funzione 'close'. Delega semplicemente 'close' a' self.key.close() '. – killthrush

0

Grazie zweiterlinde per la splendida intuizione e excellent answer provided.

ero alla ricerca di una soluzione per leggere un oggetto S3 compressa direttamente in un Pandas dataframe, e usando il suo involucro, può essere espresso in due linee:

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 non si occupa di "chi può ottenere" esso "nella minor quantità di righe", leggi la Bibbia -> "python -m this" –

Problemi correlati