2011-10-02 19 views
13

Estoy copiando un archivo de S3 a Cloudfiles, y me gustaría evitar escribir el archivo en el disco. La biblioteca Python-Cloudfiles tiene una llamada a object.stream() que parece ser lo que necesito, pero no puedo encontrar una llamada equivalente en boto. Tengo la esperanza de que iba a ser capaz de hacer algo como:¿Cómo puedo usar boto para transmitir un archivo de Amazon S3 a Rackspace Cloudfiles?

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

¿Es esto posible con boto (o supongo que cualquier otra biblioteca s3)?

+0

El [smart_open] (https://github.com/piskvorky/smart_open) biblioteca Python hace eso (tanto para leer como para escribir). – Radim

Respuesta

17

El objeto clave en el boto, que representa el objeto en S3, se puede utilizar como un iterador por lo que debe ser capaz de hacer algo como esto:

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

O, como en el caso de que su ejemplo , se puede hacer:

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

una biblioteca bien diseñada :) – ehacinom

18

Calculo al menos algunas de las personas que ven a esta pregunta será como yo, y querrá una manera de transmitir un archivo desde la línea de boto por la línea (o una coma por coma, o cualquier otro delimitador). Aquí hay una manera simple de hacerlo:

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 

@ garnaat La respuesta anterior sigue siendo excelente y 100% cierto. Espero que el mío todavía ayude a alguien.

+0

dividir en otros extremos de línea de ambos tipos con: 'lines = re.split (r '[\ n \ r] +', byte)' - útil para archivos CSV exportados desde Excel – marcfrodi

+2

uno más nota: Tuve que agregar 'yield unfinished_line' después de completar el ciclo' for byte in f: '; de lo contrario, la última línea no sería procesada – marcfrodi

+1

¿Hay una buena razón por la cual esto no es parte de la API de Boto3? Si no, ¿debería uno enviar una solicitud de extracción para solucionar esto? Estaría súper mal por noquear algo así! – lol

13

Otras respuestas en este hilo están relacionadas con boto, pero S3.Object ya no es iterable en boto3. Por lo tanto, el siguiente no funciona, se produce un mensaje de TypeError: 's3.Object' object is not iterable error:

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) 

En boto3, el contenido del objeto está disponible en S3.Object.get()['Body'] que no es un iterable tampoco, por lo que el siguiente no funciona:

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

por lo tanto, una alternativa es utilizar el método de lectura, pero esta carga el objeto S3 TODO en la memoria, que cuando se trata de archivos de gran tamaño no es siempre una posibilidad:

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

Pero el método read permite pasar el parámetro amt que especifica el número de bytes que queremos leer de la secuencia subyacente. Este método puede ser llamado varias veces hasta que toda la corriente ha sido leída:

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

Excavando en botocore.response.StreamingBody código uno se da cuenta de que la secuencia subyacente también está disponible, por lo que podría repetir la siguiente manera:

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

Mientras googlear también he visto algunos enlaces que podrían ser el uso, pero no lo he probado:

+1

Respuesta muy útil. Gracias @smallo. Aprecio que haya expuesto el __raw_stream privado, que es lo que creo que la mayoría de la gente está buscando. – saccharine

1

Este es mi solución de envoltura corporal de 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) 

Ejemplo de uso:

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