Kann ich einen Datei-Upload nach S3 ohne Header mit Inhaltslänge streamen?

70

Ich arbeite auf einem Computer mit begrenztem Speicher und möchte eine dynamisch generierte Datei (nicht von der Festplatte) in Streaming-Weise auf S3 hochladen. Mit anderen Worten, ich kenne die Dateigröße nicht, wenn ich den Upload starte, aber ich werde sie am Ende wissen. Normalerweise hat eine PUT-Anforderung einen Content-Length-Header, aber vielleicht gibt es einen Weg, dies zu umgehen, z. B. die Verwendung eines mehrteiligen oder Chunked-Content-Typs.

S3 kann Streaming-Uploads unterstützen. Zum Beispiel hier:

http://blog.odonnell.nu/posts/streaming-uploads-s3-python-and-poster/

Meine Frage ist, kann ich dasselbe erreichen, ohne die Dateilänge zu Beginn des Uploads angeben zu müssen?

Tyler
quelle
Die smart_open Python-Bibliothek erledigt das für Sie (gestreamtes Lesen und Schreiben).
Radim

Antworten:

76

Sie müssen Ihre Datei in 5MiB + Chunks über die mehrteilige API von S3 hochladen . Jeder dieser Blöcke erfordert eine Inhaltslänge, aber Sie können vermeiden, große Datenmengen (100 MB +) in den Speicher zu laden.

  • Initiieren Sie den S3 Multipart Upload .
  • Sammeln Sie Daten in einem Puffer, bis dieser Puffer die untere Blockgrößengrenze von S3 (5 MB) erreicht. Generieren Sie beim Aufbau des Puffers eine MD5-Prüfsumme.
  • Laden Sie diesen Puffer als Teil hoch, speichern Sie das ETag (lesen Sie die Dokumente dazu).
  • Sobald Sie EOF Ihrer Daten erreicht haben, laden Sie den letzten Block hoch (der kleiner als 5 MB sein kann).
  • Schließen Sie den mehrteiligen Upload ab.

S3 erlaubt bis zu 10.000 Teile. Wenn Sie also eine Teilegröße von 5 MB auswählen, können Sie dynamische Dateien mit bis zu 50 GB hochladen. Sollte für die meisten Anwendungsfälle ausreichen.

Allerdings: Wenn Sie mehr benötigen, müssen Sie Ihre Teilegröße erhöhen. Entweder durch Verwendung einer höheren Teilegröße (z. B. 10 MB) oder durch Erhöhen während des Uploads.

First 25 parts:   5MiB (total:  125MiB)
Next 25 parts:   10MiB (total:  375MiB)
Next 25 parts:   25MiB (total:    1GiB)
Next 25 parts:   50MiB (total: 2.25GiB)
After that:     100MiB

Auf diese Weise können Sie Dateien mit bis zu 1 TB hochladen (S3-Limit für eine einzelne Datei beträgt derzeit 5 TB), ohne unnötig Speicherplatz zu verschwenden.


Ein Hinweis zu Ihrem Link zum Sean O'Donnells-Blog :

Sein Problem unterscheidet sich von Ihrem - er kennt und verwendet die Inhaltslänge vor dem Hochladen. Er möchte diese Situation verbessern: Viele Bibliotheken verarbeiten Uploads, indem sie alle Daten aus einer Datei in den Speicher laden. Im Pseudocode wäre das ungefähr so:

data = File.read(file_name)
request = new S3::PutFileRequest()
request.setHeader('Content-Length', data.size)
request.setBody(data)
request.send()

Seine Lösung macht es, indem er das Content-Lengthüber die Dateisystem-API erhält . Anschließend überträgt er die Daten von der Festplatte in den Anforderungsdatenstrom. Im Pseudocode:

upload = new S3::PutFileRequestStream()
upload.writeHeader('Content-Length', File.getSize(file_name))
upload.flushHeader()

input = File.open(file_name, File::READONLY_FLAG)

while (data = input.read())
  input.write(data)
end

upload.flush()
upload.close()
Marcel Jackwerth
quelle
1
Eine Java-Implementierung in Form eines OutputStreams gibt es in s3distcp github.com/libin/s3distcp/blob/master/src/main/java/com/amazon/…
sigget
2
Ich habe eine Open-Source-Bibliothek erstellt, die sich diesem Thema
Alex Hall
1
Wo haben Sie das 5MiB-Limit gefunden?
Landon Kuhn
1
Sieht so aus, als könnten Sie die Cli jetzt auch mit Pipe verwenden - github.com/aws/aws-cli/pull/903
chrismarx
@AlexHall irgendeine Python-Implementierung?
Tushar Kolhe
8

Setzen Sie diese Antwort hier für andere ein, falls es hilft:

Wenn Sie die Länge der Daten, die Sie auf S3 streamen, nicht kennen, können Sie mit S3FileInfoihrer OpenWrite()Methode beliebige Daten in S3 schreiben.

var fileInfo = new S3FileInfo(amazonS3Client, "MyBucket", "streamed-file.txt");

using (var outputStream = fileInfo.OpenWrite())
{
    using (var streamWriter = new StreamWriter(outputStream))
    {
        streamWriter.WriteLine("Hello world");
        // You can do as many writes as you want here
    }
}
mwrichardson
quelle
1
Gibt es ein Java-Äquivalent dieser Klassen?
Steve K
Ist nicht die Länge von "Hallo Welt" bekannt? funktioniert es, wenn die Eingabe ein Stream ist?
at0mzk
Wird im Dotnet-Kern nicht unterstützt, da die Synchronität von Amazon.S3.IO apis laut Microsoft besteht.
XiaochuanQ
5

Sie können das Befehlszeilentool gof3r verwenden, um nur Linux-Pipes zu streamen:

$ tar -czf - <my_dir/> | gof3r put --bucket <s3_bucket> --key <s3_object>
Webwurst
quelle
Gibt es eine Möglichkeit, dies einfach zu tun tar -czf - <my_dir/> | aws s3 --something-or-other?
2

Wenn Sie Node.js verwenden, können Sie ein Plugin wie s3-Streaming-Upload verwenden , um dies ganz einfach zu erreichen.

Nathanpeck
quelle
1

Weitere Informationen zu mehrteiligen HTTP-Enitity-Anforderungen. Sie können eine Datei als Datenblock an das Ziel senden.

Kris
quelle