2013-04-02 18 views
7

Ho un file locale di grandi dimensioni. Voglio caricare una versione gzip di quel file in S3 usando la libreria boto. Il file è troppo grande per essere gzip in modo efficiente sul disco prima del caricamento, quindi dovrebbe essere gzip in streaming durante il caricamento.Come gzip durante il caricamento in s3 utilizzando boto

La libreria boto conosce una funzione set_contents_from_file() che si aspetta un oggetto simile a un file da cui leggerà.

La libreria gzip conosce la classe GzipFile che può ottenere un oggetto tramite il parametro denominato fileobj; scriverà su questo oggetto durante la compressione.

Mi piacerebbe combinare queste due funzioni, ma l'unica API vuole leggere da sola, l'altra API vuole scrivere da sola; né conosce un'operazione passiva (come essere scritto o letto da).

Qualcuno ha un'idea su come combinarli in modo funzionante? EDIT: Ho accettato una risposta (vedi sotto) perché mi ha suggerito dove andare, ma se hai lo stesso problema, potresti trovare la mia risposta (anche sotto) più utile, perché ho implementato una soluzione usando caricamenti multipart in esso.

risposta

3

Non c'è davvero un modo per farlo perché S3 non supporta il vero input di streaming (cioè la codifica di trasferimento chunked). È necessario conoscere la lunghezza del contenuto prima del caricamento e l'unico modo per sapere che deve aver eseguito prima l'operazione gzip.

+0

Sarà l'upload S3 davvero bisogno di conoscere la dimensione del valore? Ciò significherebbe veramente che non è possibile eseguire alcuna compressione di streaming durante la memorizzazione. Vado a controllare questo. – Alfe

+0

C'è un 'set_contents_from_stream()' nelle chiavi boto-s3-bucket. Che almeno un suggerimento su quello streaming dovrebbe essere possibile, non credi? – Alfe

+0

Dalla sua documentazione: 'L'oggetto flusso non è ricercabile e la dimensione totale non è nota. Ciò implica che non è possibile specificare Content-Size e Content-MD5 nell'intestazione. Pertanto, per gli enormi caricamenti , il ritardo nel calcolo di MD5 viene evitato ma con una penalità di impossibilità di verificare l'integrità dei dati caricati . – Alfe

18

ho implementato la soluzione accennato nei commenti della risposta accettata dalla garnaat:

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

Sembra funzionare senza problemi. E dopo tutto, lo streaming è nella maggior parte dei casi solo un chunking dei dati. In questo caso, i blocchi sono grandi circa 10 MB, ma a chi importa? Finché non stiamo parlando di diversi pezzi GB, sto bene con questo.


Aggiornamento per 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() 
Problemi correlati