it-swarm.com.de

Pipe einen Stream an s3.upload ()

Derzeit verwende ich ein node.js-Plugin namens s3-upload-stream , um sehr große Dateien an Amazon S3 zu streamen. Es verwendet die Multipart-API und funktioniert meistens sehr gut.

Dieses Modul zeigt jedoch sein Alter und ich musste bereits Änderungen daran vornehmen (der Autor hat es ebenfalls abgelehnt). Heute bin ich auf ein anderes Problem mit Amazon gestoßen, und ich würde wirklich gerne die Empfehlung des Autors nutzen und mit der offiziellen aws-sdk beginnen, um meine Uploads durchzuführen.

ABER.

Das offizielle SDK scheint das Piping zu s3.upload() nicht zu unterstützen. Das Wesen von s3.upload besteht darin, dass Sie den lesbaren Stream als Argument an den S3-Konstruktor übergeben müssen.

Ich habe ungefähr 120 Benutzercode-Module, die verschiedene Dateiverarbeitungen durchführen, und sie stehen dem Endziel ihrer Ausgabe nicht zu. Der Motor gibt ihnen einen pipefähigen, beschreibbaren Ausgabestrom, und sie leiten zu ihm. Ich kann ihnen kein AWS.S3-Objekt übergeben und sie bitten, upload() darauf aufzurufen, ohne Code zu allen Modulen hinzuzufügen. Der Grund, warum ich s3-upload-stream verwendet habe, war, dass es das Piping unterstützt.

Gibt es eine Möglichkeit, aws-sdk s3.upload() zu etwas zu machen, an das ich den Stream weiterleiten kann?

40
womp

Wickeln Sie die S3 upload()-Funktion mit dem node.js stream.PassThrough()-Stream.

Hier ist ein Beispiel:

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;
}
74
Casey Benko

In der akzeptierten Antwort endet die Funktion, bevor der Upload abgeschlossen ist, und ist daher falsch. Der folgende Code wird korrekt von einem lesbaren Stream geleitet.

Referenz hochladen

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

Sie können auch einen Schritt weitergehen und Fortschrittsinformationen mit ManagedUpload als solchen ausgeben:

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

ManagedUpload-Referenz

Eine Liste der verfügbaren Ereignisse

23
tsuz

Eine etwas verspätete Antwort könnte hoffentlich jemand anderem helfen. Sie können sowohl den beschreibbaren Stream als auch das Versprechen zurückgeben, sodass Sie nach Abschluss des Uploads Antwortdaten erhalten können.

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(),
  };
}

Und Sie können die Funktion wie folgt verwenden:

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

readStream.pipe(writeStream);
promise.then(console.log);
14
Ahmet Cetin

Typ Script Lösung: 
Dieses Beispiel verwendet: 

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

Und async-funktion:

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

Rufen Sie diese Methode wie folgt auf:

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

Wenn es jemandem hilft, konnte ich erfolgreich vom Client zu S3 streamen:

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

Der serverseitige Code setzt voraus, dass req ein Stream-Objekt ist. In meinem Fall wurde er vom Client mit Dateiinformationen in den Kopfzeilen gesendet.

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

Ja, das bricht die Konvention, aber wenn man sich den Gist ansieht, ist er viel sauberer als alles andere, was ich mit Mulder, Busboy usw. gefunden habe.

+1 für Pragmatismus und Danke an @SalehenRahman für seine Hilfe.

1
mattdlockyer

Für diejenigen, die sich beschweren, dass die s3-API-Upload-Funktion und eine Null-Byte-Datei auf S3 (@ Radar155 und @gabo) landen - hatte auch dieses Problem.

Erstellen Sie einen zweiten PassThrough-Stream, leiten Sie einfach alle Daten von der ersten zur zweiten und übergeben Sie die Referenz an diese Sekunde an s3. Sie können dies auf verschiedene Arten tun. Möglicherweise ist es eine schmutzige Methode, auf das "Daten" -Ereignis des ersten Streams zu achten und dann dieselben Daten in den zweiten Stream zu schreiben die Endefunktion im zweiten Stream. Ich habe keine Ahnung, ob es sich hierbei um einen Fehler in der aws-API, die Version des Knotens oder ein anderes Problem handelt - aber das Problem wurde für mich gelöst.

So könnte es aussehen:

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();
});
0
Tim

Keine der Antworten hat für mich funktioniert, weil ich wollte:

  • Pipe in s3.upload()
  • Leiten Sie das Ergebnis von s3.upload() in einen anderen Stream

Die akzeptierte Antwort macht das letztere nicht. Die anderen verlassen sich auf das Versprechen api, das bei der Arbeit mit Streampipes umständlich zu handhaben ist.

Dies ist meine Änderung der akzeptierten Antwort.

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
})
0
cortopy

Ich verwende KnexJS und hatte ein Problem mit der Streaming-API. Ich habe es endlich behoben, hoffentlich hilft das Folgende jemandem.

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();
0
TestWell