Posso trasmettere in streaming un file su S3 senza un’intestazione di lunghezza del contenuto?

Sto lavorando su una macchina con memoria limitata e mi piacerebbe caricare un file generato dynamicmente (non da disco) in modo streaming su S3. In altre parole, non conosco le dimensioni del file quando avvio il caricamento, ma lo saprò alla fine. Normalmente una richiesta PUT ha un’intestazione Content-Length, ma forse c’è un modo per aggirare questo, come l’uso di tipo di contenuto multipart o chunked.

S3 può supportare i caricamenti in streaming. Ad esempio, vedi qui:

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

La mia domanda è: posso realizzare la stessa cosa senza dover specificare la lunghezza del file all’inizio del caricamento?

Devi caricare il tuo file in blocchi 5MiB + tramite l’API multipart S3 . Ognuno di questi blocchi richiede un Content-Length ma è ansible evitare di caricare enormi quantità di dati (100MiB +) in memoria.

  • Avvia il caricamento multipart S3.
  • Raccogliere i dati in un buffer fino a quando il buffer raggiunge il limite inferiore della dimensione del blocco S3 (5MiB). Genera checksum MD5 durante la creazione del buffer.
  • Carica quel buffer come parte , salva ETag (leggi i documenti su quello).
  • Una volta raggiunto EOF dei tuoi dati, carica l’ultimo blocco (che può essere inferiore a 5MiB).
  • Finalizza il caricamento multipart.

S3 consente fino a 10.000 parti. Quindi, scegliendo una part-size di 5MiB, potrai caricare file dinamici fino a 50GiB. Dovrebbe essere sufficiente per la maggior parte dei casi d’uso.

Tuttavia: se hai bisogno di più, devi aumentare la dimensione della parte. O utilizzando un part-size più elevato (ad esempio 10MiB) o aumentandolo durante il caricamento.

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 

Ciò ti consentirà di caricare file fino a 1 TB (il limite di S3 per un singolo file è 5 TB in questo momento) senza sprecare inutilmente memoria.


Una nota sul tuo link al blog di Sean O’Donnells :

Il suo problema è diverso dal tuo: conosce e utilizza il Content-Length prima del caricamento. Vuole migliorare questa situazione: molte librerie gestiscono i caricamenti caricando tutti i dati da un file in memoria. In pseudo-codice che sarebbe qualcosa di simile a questo:

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

La sua soluzione lo fa ottenendo il Content-Length tramite l’API del filesystem. Quindi esegue lo streaming dei dati dal disco nel stream di richieste. Nello pseudo-codice:

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

Mettere questa risposta qui per gli altri nel caso in cui aiuta:

Se non si conosce la lunghezza dei dati che si sta trasmettendo su S3, è ansible utilizzare S3FileInfo e il OpenWrite() metodo OpenWrite() per scrivere dati arbitrari in S3.

 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 } } 

Puoi usare lo strumento da riga di comando gof3r per fare il stream dei tubi di linux:

 $ tar -czf -  | gof3r put --bucket  --key  

Fare riferimento più alle richieste di enitità multipartito HTTP. È ansible inviare un file come blocchi di dati alla destinazione.

Se stai usando Node.js puoi usare un plugin come s3-streaming-upload per farlo abbastanza facilmente.