web-dev-qa-db-fra.com

Pouvez-vous télécharger vers S3 en utilisant un flux plutôt qu'un fichier local?

Je dois créer un CSV et le télécharger dans un compartiment S3. Comme je crée le fichier à la volée, il serait préférable de l'écrire directement dans le compartiment S3 lors de sa création plutôt que d'écrire le fichier entier localement, puis de télécharger le fichier à la fin.

Y a-t-il un moyen de faire cela? Mon projet est en Python et je suis assez nouveau dans le langage. Voici ce que j'ai essayé jusqu'à présent:

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

J'ai reçu cette erreur: BotoClientError: s3 ne prend pas en charge le transfert par blocs

MISE À JOUR: J'ai trouvé un moyen d'écrire directement sur S3, mais je ne peux pas trouver un moyen d'effacer le tampon sans supprimer réellement les lignes que j'ai déjà écrites. Donc, par exemple:

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

Écrit 3 lignes dans le fichier, mais je ne peux pas libérer de mémoire pour écrire un gros fichier. Si j'ajoute:

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

dans la boucle, seule la dernière ligne du fichier est écrite. Existe-t-il un moyen de libérer des ressources sans supprimer les lignes du fichier?

29
inquiring minds

J'ai trouvé une solution à ma question, que je posterai ici au cas où quelqu'un d'autre serait intéressé. J'ai décidé de le faire en tant que parties d'un téléchargement en plusieurs parties. Vous ne pouvez pas diffuser sur S3. Il existe également un package disponible qui transforme votre fichier de streaming en un téléchargement en plusieurs parties que j'ai utilisé: 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()
26
inquiring minds

Selon docs c'est possible

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

afin que nous puissions utiliser StringIO de manière ordinaire

Mise à jour : smart_open lib de @inquiring minds réponse est une meilleure solution

4
El Ruso