python - ¿Cómo puedo usar boto para transmitir un archivo de Amazon S3 a Rackspace Cloudfiles?
amazon-s3 (5)
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())
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
Me imagino que al menos algunas de las personas que vean 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. Ojalá el mío todavía ayude a alguien.
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 utilizar 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. Este método se puede llamar repetidamente 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:
StreamingBody
de Botocore tiene un método 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)