¿Puedo transmitir una carga de archivo a S3 sin un encabezado de longitud de contenido?

Estoy trabajando en una máquina con memoria limitada, y me gustaría cargar un archivo generado dinámicamente (no desde el disco) de forma continua a S3. En otras palabras, no sé el tamaño del archivo cuando comienzo la carga, pero lo sabré para el final. Normalmente, una solicitud PUT tiene un encabezado Content-Length, pero tal vez haya una forma de eludir esto, como el uso de contenido de tipo multipart o fragmentado.

S3 puede soportar cargas de transmisión. Por ejemplo, mira aquí:

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

Mi pregunta es, ¿puedo lograr lo mismo sin tener que especificar la longitud del archivo al inicio de la carga?

Tienes que subir tu archivo en trozos de 5MiB + a través de la API de varias partes de S3 . Cada uno de esos fragmentos requiere una longitud de contenido, pero puede evitar cargar grandes cantidades de datos (100MiB +) en la memoria.

  • Inicie S3 Multipart Upload .
  • Reúna datos en un búfer hasta que ese búfer scope el límite de tamaño de porción inferior de S3 (5MiB). Genera sum de comprobación MD5 mientras construyes el buffer.
  • Suba ese búfer como una Parte , almacene el ETag (lea los documentos en ese).
  • Una vez que llegue a EOF de sus datos, cargue el último fragmento (que puede ser más pequeño que 5MiB).
  • Finalice la carga de partes múltiples.

S3 permite hasta 10,000 partes. Por lo tanto, al elegir un tamaño de 5MiB, podrá subir archivos dynamics de hasta 50GiB. Debería ser suficiente para la mayoría de los casos de uso.

Sin embargo: si necesita más, debe boost su tamaño de parte. Ya sea usando un tamaño de parte superior (10Mib por ejemplo) o incrementándolo durante la carga.

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 

Esto le permitirá cargar archivos de hasta 1TB (el límite de S3 para un solo archivo es de 5TB en este momento) sin desperdiciar memoria innecesariamente.


Una nota en su enlace al blog Sean O’Donnells :

Su problema es diferente al suyo: él conoce y usa Content-Length antes de la carga. Él quiere mejorar en esta situación: muchas bibliotecas manejan cargas cargando todos los datos de un archivo en la memoria. En pseudo-código que sería algo como esto:

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

Su solución lo hace obteniendo el Content-Length través del sistema de archivos-API. A continuación, transmite los datos del disco a la secuencia de solicitud. En pseudo-código:

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

Poniendo esta respuesta aquí para otros en caso de que ayude:

Si no conoce la longitud de los datos que está transmitiendo a S3, puede usar S3FileInfo y su método OpenWrite() para escribir datos arbitrarios en 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 } } 

Puede usar la herramienta de línea de comandos gof3r para simplemente transmitir las tuberías de Linux:

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

Consulte más sobre las solicitudes de enitity de varias partes HTTP. Puede enviar un archivo como fragmentos de datos al destino.

Si está utilizando Node.js, puede usar un complemento como s3-streaming-upload para lograr esto con bastante facilidad.