2011-10-02 12 views
13

Sto copiando un file da S3 a Cloudfiles e vorrei evitare di scrivere il file su disco. La libreria Python-Cloudfiles ha una chiamata a object.stream() che sembra essere quello di cui ho bisogno, ma non riesco a trovare una chiamata equivalente in boto. Sto sperando che sarei stato in grado di fare qualcosa di simile:Come posso usare boto per eseguire lo streaming di un file da Amazon S3 a Rackspace Cloudfiles?

shutil.copyfileobj(s3Object.stream(),rsObject.stream()) 

Questo è possibile con boto (o suppongo qualsiasi altra libreria s3)?

+0

L'(https://github.com/piskvorky/smart_open) libreria Python [smart_open] fa quello (sia per leggere che per scrivere). – Radim

risposta

17

l'oggetto chiave in boto, che rappresenta su un oggetto in S3, può essere utilizzato come un iteratore così si dovrebbe essere in grado di fare qualcosa del genere:

>>> import boto 
>>> c = boto.connect_s3() 
>>> bucket = c.lookup('garnaat_pub') 
>>> key = bucket.lookup('Scan1.jpg') 
>>> for bytes in key: 
... write bytes to output stream 

Oppure, come nel caso del vostro esempio , si potrebbe fare:

>>> shutil.copyfileobj(key, rsObject.stream()) 
+0

una libreria così ben progettata :) – ehacinom

18

ho dato, almeno alcune delle persone che vedono questa domanda sarà come me, e vuole un modo per lo streaming di un file dalla linea di boto per riga (o una virgola per virgola, o qualsiasi altro delimitatore). Ecco un modo semplice per farlo:

def getS3ResultsAsIterator(self, aws_access_info, key, prefix):   
    s3_conn = S3Connection(**aws_access) 
    bucket_obj = s3_conn.get_bucket(key) 
    # go through the list of files in the key 
    for f in bucket_obj.list(prefix=prefix): 
     unfinished_line = '' 
     for byte in f: 
      byte = unfinished_line + byte 
      #split on whatever, or use a regex with re.split() 
      lines = byte.split('\n') 
      unfinished_line = lines.pop() 
      for line in lines: 
       yield line 

@ risposta di garnaat sopra è ancora grande e vero al 100%. Spero che il mio aiuti ancora qualcuno.

+0

divisa su altri due tipi di terminazioni di riga con: 'lines = re.split (r '[\ n \ r] +', byte)' - utile per i file CSV esportati da Excel – marcfrodi

+2

un altro nota: ho dovuto aggiungere 'yield unfinished_line' dopo che il' per byte in f: 'il ciclo era completo, altrimenti l'ultima riga non sarebbe stata elaborata – marcfrodi

+1

C'è una buona ragione per cui questo non fa parte dell'API Boto3? In caso contrario, si dovrebbe inviare una richiesta di pull per risolvere questo problema? Sarei fantastico per aver bussato a qualcosa del genere! – lol

13

Altre risposte in questo thread sono correlate a boto, ma S3.Object non è più ripetibile in boto3. Quindi, quanto segue non funziona, produce un messaggio di TypeError: 's3.Object' object is not iterable errore:

s3 = boto3.session.Session(profile_name=my_profile).resource('s3') 
    s3_obj = s3.Object(bucket_name=my_bucket, key=my_key) 

    with io.FileIO('sample.txt', 'w') as file: 
     for i in s3_obj: 
      file.write(i) 

In boto3, i contenuti dell'oggetto sono disponibili sul S3.Object.get()['Body'] che non è un iterabile o, in modo quanto segue NON funzionano ancora:

body = s3_obj.get()['Body'] 
    with io.FileIO('sample.txt', 'w') as file: 
     for i in body: 
      file.write(i) 

Così, in alternativa è quella di utilizzare il metodo di lettura, ma questo carica l'oggetto S3 TUTTO in memoria, che quando si tratta di file di grandi dimensioni non è sempre una possibilità:

body = s3_obj.get()['Body'] 
    with io.FileIO('sample.txt', 'w') as file: 
     for i in body.read(): 
      file.write(i) 

Ma il metodo read consente di passare il parametro amt specificando il numero di byte che si desidera leggere dal flusso sottostante. Questo metodo può essere ripetutamente chiamato finché l'intero flusso è stato letto:

body = s3_obj.get()['Body'] 
    with io.FileIO('sample.txt', 'w') as file: 
     while file.write(body.read(amt=512)): 
      pass 

Scavare botocore.response.StreamingBody codice si accorge che il flusso sottostante è inoltre disponibile, così abbiamo potuto scorrere come segue:

body = s3_obj.get()['Body'] 
    with io.FileIO('sample.txt', 'w') as file: 
     for b in body._raw_stream: 
      file.write(b) 

Mentre googling ho visto anche alcuni link che potrebbero essere l'uso, ma non ho provato:

+1

Risposta molto utile Grazie @smallo. Apprezzo che tu abbia esposto il __raw_stream privato, che è quello che penso che la maggior parte delle persone stia cercando. – saccharine

1

Questa è la mia soluzione di avvolgimento del corpo in streaming:

import io 
class S3ObjectInterator(io.RawIOBase): 
    def __init__(self, bucket, key): 
     """Initialize with S3 bucket and key names""" 
     self.s3c = boto3.client('s3') 
     self.obj_stream = self.s3c.get_object(Bucket=bucket, Key=key)['Body'] 

    def read(self, n=-1): 
     """Read from the stream""" 
     return self.obj_stream.read() if n == -1 else self.obj_stream.read(n) 

Esempio utilizzo:

obj_stream = S3ObjectInterator(bucket, key) 
for line in obj_stream: 
    print line 
Problemi correlati