2015-06-24 7 views
17

Ich muss eine CSV erstellen und in einen S3-Bucket hochladen. Da ich die Datei im Handumdrehen erstelle, wäre es besser, wenn ich sie direkt in S3-Bucket schreiben könnte, während sie erstellt wird, anstatt die ganze Datei lokal zu schreiben und dann die Datei am Ende hochzuladen.Können Sie mit einem Stream anstelle einer lokalen Datei in S3 hochladen?

Gibt es eine Möglichkeit, dies zu tun? Mein Projekt ist in Python und ich bin ziemlich neu in der Sprache. Hier ist, was ich versuchte, so weit:

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

ich diesen Fehler erhalten: BotoClientError: s3 nicht segmentierte Übertragungs unterstützt

UPDATE: Ich einen Weg gefunden direkt auf S3 zu schreiben, aber ich kann nicht finde einen Weg, den Puffer zu löschen, ohne die Zeilen zu löschen, die ich bereits geschrieben habe. So zum Beispiel:

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

Schreibt 3 Zeilen in die Datei, aber ich bin nicht in der Lage Speicher freizugeben eine große Datei zu schreiben. Wenn ich hinzufügen:

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

in die Schleife, dann wird nur die letzte Zeile der Datei geschrieben. Gibt es eine Möglichkeit, Ressourcen freizugeben, ohne Zeilen aus der Datei zu löschen?

+0

Auch StringIO in gewöhnlicher Weise verwenden, wenn Sie S3 schreiben könnte, wie Sie wollen, würde ich es nicht auf Konsistenz Herausforderungen empfehlen . Warum denkst du, es wäre besser, nicht lokal zu schreiben? Möchten Sie ein teilweises S3-Objekt, wenn eine Ausnahme oder ein Problem aufgetreten ist? Ich nehme nicht an. – cgseller

+1

Ich wollte direkt schreiben, um ein wenig effizienter zu sein. Im Wesentlichen, wenn ich die Datei lokal schreibe und hochlade, füge ich das Hochladen als zusätzlichen Schritt hinzu und bereinige die lokale Datei. Es macht mir nichts aus, eine unvollständige Datei zu haben - ich könnte eine unvollständige Datei haben, wenn ich sie auch lokal schreibe. Das System wird idempotent sein und entweder eine Datei in einem Fehlerzustand löschen oder fortsetzen. –

Antwort

16

Ich habe eine Lösung für meine Frage gefunden, die ich hier veröffentlichen werde, falls jemand anderes interessiert ist. Ich habe mich dazu entschlossen, dies als Teile in einem mehrteiligen Upload zu tun. Sie können nicht zu S3 streamen. Es gibt auch ein Paket, das Ihre Streaming-Datei in einen mehrteiligen Upload umwandelt, den ich verwendet habe: 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() 
+0

Achten Sie bei Python 2 darauf, 'StringIO.StringIO()' anstelle von 'io.StringIO()' zu verwenden, sonst erhalten Sie einen Kodierungsfehler – Anconia

5

Nach docs ist es möglich

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

so können wir

+1

Ich verstehe nicht, wie man das benutzt. Ist /tmp/hello.txt nicht eine lokale Datei, die wir zu vermeiden versuchen? – EthanP

+0

@EthanP [StringIO] (https://docs.python.org/2/library/stringio.html) - Lesen und schreiben Sie Strings als Dateien. Verwenden Sie das 'StringIO'-Objekt anstelle der Datei –

+0

Nein, gemäß [dieses Ticket] (https://github.com/boto/boto3/issues/256) wird es nicht unterstützt. Die Idee, Streams mit S3 zu verwenden, besteht darin, die Verwendung von statischen Dateien zu vermeiden, wenn große Dateien von einigen Gigabyte hochgeladen werden müssen. Ich versuche auch, dieses Problem zu lösen - ich muss eine große Daten von mongodb lesen und S3, ich möchte keine Dateien verwenden. – baldr