putobject nodejs node files aws node.js amazon-web-services amazon-s3 node-streams

node.js - nodejs - s3 upload file



Canalizar una secuencia a s3.upload() (10)

En la respuesta aceptada, la función finaliza antes de que se complete la carga y, por lo tanto, es incorrecta. El siguiente código se canaliza correctamente desde una secuencia legible.

Subir referencia

const uploadStream = ({ Bucket, Key }) => { const s3 = new AWS.S3(); const pass = new stream.PassThrough(); return { writeStream: pass, promise: s3.upload({ Bucket, Key, Body: pass }).promise(), }; }

También puede ir un paso más allá y generar información de progreso utilizando ManagedUpload como tal:

const { writeStream, promise } = uploadStream({Bucket: ''yourbucket'', Key: ''yourfile.mp4''}); const readStream = fs.createReadStream(''/path/to/yourfile.mp4''); readStream.pipe(writeStream); promise.then(console.log);

Referencia de carga administrada

Una lista de eventos disponibles.

Actualmente estoy usando un complemento node.js llamado s3-upload-stream para transmitir archivos muy grandes a Amazon S3. Utiliza la API multiparte y en su mayor parte funciona muy bien.

Sin embargo, este módulo muestra su antigüedad y ya he tenido que modificarlo (el autor también lo ha desaprobado). Hoy me encontré con otro problema con Amazon, y realmente me gustaría tomar la recomendación del autor y comenzar a usar el aws-sdk oficial para lograr mis cargas.

PERO.

El SDK oficial no parece admitir tuberías a s3.upload() . La naturaleza de s3.upload es que debe pasar la secuencia legible como argumento al constructor S3.

Tengo aproximadamente más de 120 módulos de código de usuario que realizan varios procesos de archivos, y son independientes del destino final de su salida. El motor les entrega un flujo de salida grabable y grabable, y lo canalizan. No puedo entregarles un objeto AWS.S3 y pedirles que llamen upload() en él sin agregar código a todos los módulos. La razón por la que usé s3-upload-stream fue porque soportaba tuberías.

¿Hay alguna manera de hacer aws-sdk s3.upload() algo a lo que pueda canalizar la transmisión?


Envuelva la función upload() S3 con el stream stream.PassThrough() .

Aquí hay un ejemplo:

inputStream .pipe(uploadFromStream(s3)); function uploadFromStream(s3) { var pass = new stream.PassThrough(); var params = {Bucket: BUCKET, Key: KEY, Body: pass}; s3.upload(params, function(err, data) { console.log(err, data); }); return pass; }


Estoy usando KnexJS y tuve un problema con su API de transmisión. Finalmente lo arreglé, espero que lo siguiente ayude a alguien.

const knexStream = knex.select(''*'').from(''my_table'').stream(); const passThroughStream = new stream.PassThrough(); knexStream.on(''data'', (chunk) => passThroughStream.write(JSON.stringify(chunk) + ''/n'')); knexStream.on(''end'', () => passThroughStream.end()); const uploadResult = await s3 .upload({ Bucket: ''my-bucket'', Key: ''stream-test.txt'', Body: passThroughStream }) .promise();


Lo que hay que tener en cuenta en la respuesta más aceptada anteriormente es que: debe devolver el pase en la función si está utilizando tubería como,

fs.createReadStream(<filePath>).pipe(anyUploadFunction())

s3Client.putObject(''my-bucketname'', ''my-objectname.ogg'', stream, size, ''audio/ogg'', function(e) { if (e) { return console.log(e) } console.log("Successfully uploaded the stream") })

De lo contrario, se moverá silenciosamente al siguiente sin generar un error o arrojará un error de TypeError: dest.on is not a function dependiendo de cómo haya escrito la función


Ninguna de las respuestas funcionó para mí porque quería:

  • Tubería en s3.upload()
  • s3.upload() el resultado de s3.upload() en otra secuencia

La respuesta aceptada no hace lo último. Los otros confían en la API de promesa, que es engorrosa de trabajar cuando se trabaja con tuberías de flujo.

Esta es mi modificación de la respuesta aceptada.

const s3 = new S3(); function writeToS3({Key, Bucket}) { const Body = new stream.PassThrough(); s3.upload({ Body, Key, Bucket: process.env.adpBucket }) .on(''httpUploadProgress'', progress => { console.log(''progress'', progress); }) .send((err, data) => { if (err) { Body.destroy(err); } else { console.log(`File uploaded and available at ${data.Location}`); Body.destroy(); } }); return Body; } const pipeline = myReadableStream.pipe(writeToS3({Key, Bucket}); pipeline.on(''close'', () => { // upload finished, do something else }) pipeline.on(''error'', () => { // upload wasn''t successful. Handle it })


Para aquellos que se quejan de que cuando usan la función de carga s3 api y un archivo de cero bytes termina en s3 (@ Radar155 y @gabo), también tuve este problema.

Cree una segunda secuencia PassThrough y simplemente canalice todos los datos del primero al segundo y pase la referencia a ese segundo a s3. Puede hacer esto de dos maneras diferentes, posiblemente una manera sucia es escuchar el evento "datos" en la primera transmisión y luego escribir esos mismos datos en la segunda transmisión, lo mismo para el evento "final", simplemente llame la función final en la segunda secuencia. No tengo idea de si esto es un error en la api de aws, la versión del nodo o algún otro problema, pero funcionó para mí.

Así es como podría verse:

var PassThroughStream = require(''stream'').PassThrough; var srcStream = new PassThroughStream(); var rstream = fs.createReadStream(''Learning/stocktest.json''); var sameStream = rstream.pipe(srcStream); // interesting note: (srcStream == sameStream) at this point var destStream = new PassThroughStream(); // call your s3.upload function here - passing in the destStream as the Body parameter srcStream.on(''data'', function (chunk) { destStream.write(chunk); }); srcStream.on(''end'', function () { dataStream.end(); });


Si ayuda a alguien, pude transmitir desde el cliente a s3 con éxito:

https://gist.github.com/mattlockyer/532291b6194f6d9ca40cb82564db9d2a

El código del lado del servidor supone que req es un objeto continuo, en mi caso fue enviado desde el cliente con la información del archivo establecida en los encabezados.

const fileUploadStream = (req, res) => { //get "body" args from header const { id, fn } = JSON.parse(req.get(''body'')); const Key = id + ''/'' + fn; //upload to s3 folder "id" with filename === fn const params = { Key, Bucket: bucketName, //set somewhere Body: req, //req is a stream }; s3.upload(params, (err, data) => { if (err) { res.send(''Error Uploading Data: '' + JSON.stringify(err) + ''/n'' + JSON.stringify(err.stack)); } else { res.send(Key); } }); };

Sí, rompe la convención, pero si miras la esencia, es mucho más limpia que cualquier otra cosa que encontré usando multer, busboy, etc.

+1 por pragmatismo y gracias a @SalehenRahman por su ayuda.


Si conoce el tamaño de la transmisión, puede usar minio-js para cargar la transmisión de esta manera:

s3Client.putObject(''my-bucketname'', ''my-objectname.ogg'', stream, size, ''audio/ogg'', function(e) { if (e) { return console.log(e) } console.log("Successfully uploaded the stream") })


Solución de tipo de script:
Este ejemplo usa:

import * as AWS from "aws-sdk"; import * as fsExtra from "fs-extra"; import * as zlib from "zlib"; import * as stream from "stream";

Y función asíncrona:

public async saveFile(filePath: string, s3Bucket: AWS.S3, key: string, bucketName: string): Promise<boolean> { const uploadStream = (S3: AWS.S3, Bucket: string, Key: string) => { const passT = new stream.PassThrough(); return { writeStream: passT, promise: S3.upload({ Bucket, Key, Body: passT }).promise(), }; }; const { writeStream, promise } = uploadStream(s3Bucket, bucketName, key); fsExtra.createReadStream(filePath).pipe(writeStream); // NOTE: Addition You can compress to zip by .pipe(zlib.createGzip()).pipe(writeStream) let output = true; await promise.catch((reason)=> { output = false; console.log(reason);}); return output; }

Llame a este método en algún lugar como:

let result = await saveFileToS3(testFilePath, someS3Bucket, someKey, someBucketName);


Una respuesta un poco tardía, podría ayudar a alguien más con suerte. Puede devolver tanto la secuencia de escritura como la promesa, para que pueda obtener datos de respuesta cuando finalice la carga.

async function uploadReadableStream(stream) { const params = {Bucket: bucket, Key: key, Body: stream}; return s3.upload(params).promise(); } async function upload() { const readable = getSomeReadableStream(); const results = await uploadReadableStream(readable); console.log(''upload complete'', results); }

Y puede usar la función de la siguiente manera:

const manager = s3.upload(params); manager.on(''httpUploadProgress'', (progress) => { console.log(''progress'', progress) // { loaded: 4915, total: 192915, part: 1, key: ''foo.jpg'' } });