2013-04-02 5 views
7

Ich habe eine große lokale Datei. Ich möchte eine gezippte Version dieser Datei mit der boto-Bibliothek in S3 hochladen. Die Datei ist zu groß, um sie vor dem Hochladen effizient auf der Festplatte zu speichern. Daher sollte sie während des Uploads gezippt werden. Die Bibliothek boto kennt eine Funktion set_contents_from_file(), die ein dateiähnliches Objekt erwartet, von dem sie gelesen wird.Wie gzip beim Hochladen in s3 mit Boto

Die Bibliothek gzip kennt die Klasse GzipFile, die über den Parameter fileobj ein Objekt erhalten kann; Es wird beim Komprimieren in dieses Objekt schreiben.

Ich möchte diese beiden Funktionen kombinieren, aber die eine API möchte selbst lesen, die andere API möchte selbst schreiben; keiner kennt eine passive Operation (wie geschrieben oder gelesen wird).

Hat jemand eine Idee, wie man diese funktionell kombiniert?

EDIT: Ich akzeptierte eine Antwort (siehe unten), weil es mich anzeigte, wohin ich gehen sollte, aber wenn Sie das gleiche Problem haben, finden Sie vielleicht meine eigene Antwort (auch unten) hilfreicher, weil ich eine Lösung mit implementiert habe mehrteilige Uploads darin.

Antwort

3

Es gibt wirklich keine Möglichkeit, dies zu tun, weil S3 keine echte Streaming-Eingabe unterstützt (d. H. Chunked-Transfer-Codierung). Sie müssen die Inhaltslänge vor dem Hochladen kennen und wissen nur, dass die gzip-Operation zuerst ausgeführt wurde.

+0

Wird das S3-Upload wirklich brauchen, um die Größe des Wertes wissen? Das würde wirklich bedeuten, dass keine Streaming-Kompression während des Speicherns durchgeführt werden könnte. Ich werde nachsehen. – Alfe

+0

Es gibt einen 'set_contents_from_stream()' in den boto-s3-bucket-keys. Das zumindest deutet darauf hin, dass Streaming möglich sein sollte, meinst du nicht? – Alfe

+0

Aus seiner Dokumentation: 'Das Stream-Objekt ist nicht suchbar und die Gesamtgröße ist nicht bekannt. Dies hat die Implikation, dass wir die Content-Size und Content-MD5 in der Kopfzeile nicht angeben können. Für große Uploads wird die Verzögerung bei der Berechnung von MD5 vermieden, aber mit einer Strafe der Unfähigkeit, die Integrität der hochgeladenen Daten zu verifizieren. – Alfe

18

ich die Lösung implementiert in den Kommentaren der akzeptierte Antwort von garnaat angedeutet:

import cStringIO 
import gzip 

def sendFileGz(bucket, key, fileName, suffix='.gz'): 
    key += suffix 
    mpu = bucket.initiate_multipart_upload(key) 
    stream = cStringIO.StringIO() 
    compressor = gzip.GzipFile(fileobj=stream, mode='w') 

    def uploadPart(partCount=[0]): 
     partCount[0] += 1 
     stream.seek(0) 
     mpu.upload_part_from_file(stream, partCount[0]) 
     stream.seek(0) 
     stream.truncate() 

    with file(fileName) as inputFile: 
     while True: # until EOF 
      chunk = inputFile.read(8192) 
      if not chunk: # EOF? 
       compressor.close() 
       uploadPart() 
       mpu.complete_upload() 
       break 
      compressor.write(chunk) 
      if stream.tell() > 10<<20: # min size for multipart upload is 5242880 
       uploadPart() 

Es scheint, ohne Probleme zu arbeiten. Und schließlich ist Streaming in den meisten Fällen nur ein Chunking der Daten. In diesem Fall sind die Chunks etwa 10 MB groß, aber wen interessiert das? Solange wir nicht über mehrere GB-Chunks sprechen, geht es mir gut.


Update für Python 3:

from io import BytesIO 
import gzip 

def sendFileGz(bucket, key, fileName, suffix='.gz'): 
    key += suffix 
    mpu = bucket.initiate_multipart_upload(key) 
    stream = BytesIO() 
    compressor = gzip.GzipFile(fileobj=stream, mode='w') 

    def uploadPart(partCount=[0]): 
     partCount[0] += 1 
     stream.seek(0) 
     mpu.upload_part_from_file(stream, partCount[0]) 
     stream.seek(0) 
     stream.truncate() 

    with open(fileName, "rb") as inputFile: 
     while True: # until EOF 
      chunk = inputFile.read(8192) 
      if not chunk: # EOF? 
       compressor.close() 
       uploadPart() 
       mpu.complete_upload() 
       break 
      compressor.write(chunk) 
      if stream.tell() > 10<<20: # min size for multipart upload is 5242880 
       uploadPart() 
Verwandte Themen