¿Cómo puedo usar boto para transmitir un archivo de Amazon S3 a Rackspace Cloudfiles?

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 object.stream () que parece ser lo que necesito, pero no puedo encontrar una llamada equivalente en boto. Espero poder hacer algo como:

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

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

El objeto Clave en boto, que representa el objeto en S3, se puede usar como un iterador, por lo que debería poder 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 tu ejemplo, podrías hacer:

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

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

  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 tampoco es iterable, por lo que lo 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 usar el método de lectura, pero esto carga el objeto SÓLO S3 en la memoria, que cuando se trata de archivos grandes no siempre es 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 de read permite pasar el parámetro amt que especifica el número de bytes que queremos leer del flujo subyacente. Se puede llamar repetidamente a este método hasta que se haya leído la secuencia completa:

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

Al excavar en el código botocore.response.StreamingBody , uno se da cuenta de que la secuencia subyacente también está disponible, por lo que podríamos iterar de 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 busco en Google, también he visto algunos enlaces que podrían usarse, pero no lo he intentado:

  • WrappedStreamingBody
  • Otro hilo relacionado
  • Un problema en boto3 github para solicitar StreamingBody es un flujo adecuado , que se ha cerrado.

Creo que al menos algunas de las personas que ven esta pregunta serán como yo, y querrán una forma de transmitir un archivo de línea a línea (o coma por coma, o cualquier otro delimitador). Aquí hay una manera simple de hacer eso:

 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 

La respuesta de @garnaat arriba sigue siendo excelente y 100% verdadera. Esperemos que el mío todavía ayude a alguien.

Esta es mi solución para envolver el cuerpo del 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 

StreamingBody de Botocore tiene un método iter_lines() :

https://botocore.amazonaws.com/v1/documentation/api/latest/reference/response.html#botocore.response.StreamingBody.iter_lines

Asi que:

 import boto3 s3r = boto3.resource('s3') iterator = s3r.Object(bucket, key).get()['Body'].iter_lines() for line in iterator: print(line)