web-dev-qa-db-de.com

Versprechen mit Streams in node.js verwenden

Ich habe ein einfaches Dienstprogramm überarbeitet, um Versprechungen zu verwenden. Es holt ein pdf aus dem Web und speichert es auf der Festplatte. Die Datei sollte dann in einem PDF-Viewer geöffnet werden, sobald sie auf der Festplatte gespeichert wurde. Die Datei wird auf der Festplatte angezeigt und ist gültig. Mit dem Shell-Befehl wird die OSX-Vorschauanwendung geöffnet. In einem Dialogfeld wird jedoch angezeigt, dass die Datei leer ist.

Was ist der beste Weg, die Shell-Funktion auszuführen, nachdem der Dateistream auf die Festplatte geschrieben wurde?

// download a pdf and save to disk
// open pdf in osx preview for example
download_pdf()
  .then(function(path) {
    Shell.exec('open ' + path).code !== 0);
  });

function download_pdf() {
  const path = '/local/some.pdf';
  const url = 'http://somewebsite/some.pdf';
  const stream = request(url);
  const write = stream.pipe(fs.createWriteStream(path))
  return streamToPromise(stream);
}

function streamToPromise(stream) {
  return new Promise(function(resolve, reject) {
    // resolve with location of saved file
    stream.on("end", resolve(stream.dests[0].path));
    stream.on("error", reject);
  })
}
6
pompon

In dieser Zeile

stream.on("end", resolve(stream.dests[0].path));

sie führen sofort resolve aus, und das result des Aufrufs von resolve (das undefiniert ist, weil resolve dies zurückgibt) wird als Argument für stream.on verwendet - nicht das, was Sie wollen, richtig.

Das zweite Argument von .on muss eine function sein und nicht das Ergebnis des Aufrufs einer Funktion

Daher muss der Code sein

stream.on("end", () => resolve(stream.dests[0].path));

oder, wenn du alte Schule bist:

stream.on("end", function () { resolve(stream.dests[0].path); });

eine andere alte Schule wäre so etwas

stream.on("end", resolve.bind(null, stream.dests[0].path)); </ s>

Nein, tu das nicht: siehe Kommentare

10
Jaromanda X

Nach ein paar Versuchen habe ich eine Lösung gefunden, die immer gut funktioniert. Weitere Informationen finden Sie in den JSDoc-Kommentaren.

/**
 * Streams input to output and resolves only after stream has successfully ended.
 * Closes the output stream in success and error cases.
 * @param input {stream.Readable} Read from
 * @param output {stream.Writable} Write to
 * @return Promise Resolves only after the output stream is "end"ed or "finish"ed.
 */
function promisifiedPipe(input, output) {
    let ended = false;
    function end() {
        if (!ended) {
            ended = true;
            output.close && output.close();
            input.close && input.close();
            return true;
        }
    }

    return new Promise((resolve, reject) => {
        input.pipe(output);
        input.on('error', errorEnding);

        function niceEnding() {
            if (end()) resolve();
        }

        function errorEnding(error) {
            if (end()) reject(error);
        }

        output.on('finish', niceEnding);
        output.on('end', niceEnding);
        output.on('error', errorEnding);
    });
};

Anwendungsbeispiel:

function downloadFile(req, res, next) {
  promisifiedPipe(fs.createReadStream(req.params.file), res).catch(next);
}
4
Vasyl Boroviak

Die andere Lösung kann so aussehen:

const streamAsPromise = (readable) => {
  const result = []
  const w = new Writable({
    write(chunk, encoding, callback) {·
      result.Push(chunk)
      callback()
    }
  })
  readable.pipe(w)
  return new Promise((resolve, reject) => {
    w.on('finish', resolve)
    w.on('error', reject)
  }).then(() => result.join(''))
}

und du kannst es verwenden wie:

streamAsPromise(fs.createReadStream('secrets')).then(() => console.log(res))
1
kharandziuk