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