python csv amazon-s3 boto buffering

python - ¿Se puede subir a S3 usando una transmisión en lugar de un archivo local?



csv amazon-s3 (2)

Necesito crear un CSV y subirlo a un cubo S3. Ya que estoy creando el archivo sobre la marcha, sería mejor si pudiera escribirlo directamente en el contenedor S3, ya que se está creando en lugar de escribir el archivo completo localmente y luego cargar el archivo al final.

¿Hay alguna forma de hacer esto? Mi proyecto está en Python y soy bastante nuevo en el idioma. Aquí está lo que he intentado hasta ahora:

import csv import csv import io import boto from boto.s3.key import Key conn = boto.connect_s3() bucket = conn.get_bucket(''dev-vs'') k = Key(bucket) k.key = ''foo/foobar'' fieldnames = [''first_name'', ''last_name''] writer = csv.DictWriter(io.StringIO(), fieldnames=fieldnames) k.set_contents_from_stream(writer.writeheader())

Recibí este error: BotoClientError: s3 no admite la transferencia fragmentada

ACTUALIZACIÓN: Encontré una forma de escribir directamente en S3, pero no puedo encontrar una manera de borrar el búfer sin eliminar realmente las líneas que ya escribí. Así por ejemplo:

conn = boto.connect_s3() bucket = conn.get_bucket(''dev-vs'') k = Key(bucket) k.key = ''foo/foobar'' testDict = [{ "fieldA": "8", "fieldB": None, "fieldC": "888888888888"}, { "fieldA": "9", "fieldB": None, "fieldC": "99999999999"}] f = io.StringIO() fieldnames = [''fieldA'', ''fieldB'', ''fieldC''] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() k.set_contents_from_string(f.getvalue()) for row in testDict: writer.writerow(row) k.set_contents_from_string(f.getvalue()) f.close()

Escribe 3 líneas en el archivo, sin embargo no puedo liberar memoria para escribir un archivo grande. Si agrego:

f.seek(0) f.truncate(0)

al bucle, entonces solo se escribe la última línea del archivo. ¿Hay alguna forma de liberar recursos sin eliminar líneas del archivo?


Encontré una solución a mi pregunta, que publicaré aquí en caso de que alguien más esté interesado. Decidí hacer esto como partes en una carga multiparte. No puedes transmitir a S3. También hay un paquete disponible que cambia su archivo de transmisión a una carga de varias partes que utilicé: Smart Open .

import smart_open import io import csv testDict = [{ "fieldA": "8", "fieldB": None, "fieldC": "888888888888"}, { "fieldA": "9", "fieldB": None, "fieldC": "99999999999"}] fieldnames = [''fieldA'', ''fieldB'', ''fieldC''] f = io.StringIO() with smart_open.smart_open(''s3://dev-test/bar/foo.csv'', ''wb'') as fout: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() fout.write(f.getvalue()) for row in testDict: f.seek(0) f.truncate(0) writer.writerow(row) fout.write(f.getvalue()) f.close()


Según docs es posible

s3.Object(''mybucket'', ''hello.txt'').put(Body=open(''/tmp/hello.txt'', ''rb''))

para que podamos usar StringIO de manera ordinaria

Actualización : smart_open lib from @inquiring mind answer es la mejor solución