2011-10-02 15 views
13

Skopiuję plik z S3 do Cloudfiles i chciałbym uniknąć zapisywania pliku na dysk. Biblioteka Python-Cloudfiles ma wywołanie object.stream(), które wygląda na to, czego potrzebuję, ale nie mogę znaleźć równoważnego wywołania w boto. Mam nadzieję, że będę w stanie zrobić coś takiego:Jak mogę użyć boto do streamowania pliku z Amazon S3 do Rackspace Cloudfiles?

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

Czy to możliwe z boto (lub dowolny inny Przypuszczam s3 biblioteki)?

+0

Sieć [smart_open] (https://github.com/piskvorky/smart_open) Biblioteka Pythona robi to (zarówno do czytania, jak i pisania). – Radim

Odpowiedz

17

klucza obiektu w boto, który reprezentuje na obiekcie w S3, może być używany jak iteratora, więc powinieneś być w stanie zrobić coś takiego:

>>> 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 

Albo, jak w przypadku Twojego przykład , można zrobić:

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

tak dobrze zaprojektowana biblioteka :) – ehacinom

18

i postać przynajmniej niektórzy ludzie widząc to pytanie będzie tak jak ja, i będą chcieli sposób do strumienia pliku z linii boto po linii (lub przecinek przecinkiem lub dowolny inny ogranicznik). Oto prosty sposób:

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 

@ Odpowiedź powyżej Garnaat jest nadal świetna i w 100% prawdziwa. Mam nadzieję, że moje wciąż pomaga komuś.

+0

podzieliłem na oba typy wierszy kończących na: 'lines = re.split (r '[\ n \ r] +', byte)' - pomocne dla plików CSV wyeksportowanych z Excela – marcfrodi

+2

jeszcze jeden uwaga: Musiałem dodać 'yield unfinished_line' po zakończeniu pętli' for byte in f: ', w przeciwnym razie ostatnia linia nie zostałaby przetworzona – marcfrodi

+1

Czy istnieje dobry powód, dla którego nie jest to częścią API Boto3? Jeśli nie, czy należy przesłać żądanie pobrania, aby to naprawić? Byłbym super, jeśli chodzi o coś takiego! – lol

13

Inne odpowiedzi w tym wątku są powiązane z boto, ale S3.Object nie jest już iterowalne w boto3. Więc Następujące nie działa, to wytwarza się komunikat TypeError: 's3.Object' object is not iterable błędzie:

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) 

W boto3 zawartość obiektu jest dostępny na S3.Object.get()['Body'] który nie jest iterable albo, więc dodaje się jeszcze nie działa:

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

więc alternatywą jest użycie metody odczytu, ale wczytuje całego obiektu S3 w pamięci, które po czynienia z dużymi plikami nie zawsze jest możliwość:

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

Jednak metoda read pozwala na przekazanie parametru amt określającego liczbę bajtów, które chcemy odczytać z podstawowego strumienia. Metoda ta może być wielokrotnie nazywany aż cały strumień został przeczytany:

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

kopanie w kodzie botocore.response.StreamingBody zdamy sobie sprawę, że ogólny strumień dostępny jest również, więc możemy iteracyjne następująco:

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

Podczas googlowania ja również widziałem kilka linków, które mogą być użyte, ale nie próbowałem:

+1

Bardzo przydatna odpowiedź. Dzięki @smallo. Doceniam to, że ujawniłeś prywatny __raw_stream, który jest moim zdaniem najbardziej poszukiwanym. – saccharine

1

to my roztwór opakowania strumieniowego ciała:

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) 

Przykład użycia:

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