Я читаю, бит за битом, все файлы PNG в каталоге, и мне нужно обобщить некоторые данные в формате json. Проблема состоит в том, что, если я понимаю, программа чтения PNG отправляет асинхронное событие, "проанализированное", когда закончено. Это потому, что функция выходит из-за заселения JSON...
Я использую узел 6.11.5, поэтому я не могу использовать sync/await.
var fs = require('fs'),
PNG = require('pngjs').PNG;
exports.movie = functions.https.onRequest((req, res) => {
console.log('********** START FUNCTION ************');
var movieFolder = 1;
if (req.query.id) movieFolder = '../movies/' + req.query.id + '/png/';
var exitJson = [];
fs.readdir(movieFolder, (err, files) => {
files.forEach((file) => {
fs.createReadStream(movieFolder + file)
.pipe(new PNG({
filterType: 1
}))
.on('parsed', function () {
console.log('Parsing: ' + movieFolder + file);
exitJson.push({
width: this.width,
height: this.height,
data: []
});
});
});
});
console.log('************* FINISHED *************');
res.status(200).json(exitJson);
});
Вы можете использовать простой счетчик itemsProcessed
чтобы определить, были ли разрешены все ваши обратные вызовы.
var movieFolder = 1;
if (req.query.id) movieFolder = '../movies/' + req.query.id + '/png/';
var exitJson = [];
var itemsProcessed = 0;
fs.readdir(movieFolder, (err, files) => {
files.forEach((file) => {
fs.createReadStream(movieFolder + file)
.pipe(new PNG({
filterType: 1
}))
.on('parsed', function () {
console.log('Parsing: ' + movieFolder + file);
exitJson.push({
width: this.width,
height: this.height,
data: []
});
itemsProcessed++;
if (itemsProcessed === files.length) {
console.log('************* FINISHED *************');
res.status(200).json(exitJson);
}
});
});
});
Вы можете загружать файлы один за другим через рекурсивные вызовы. Не забудьте проверить ошибки.
exports.movie = functions.https.onRequest((req, res) => {
var movieFolder = 1;
if (req.query.id)
movieFolder = '../movies/' + req.query.id + '/png/';
var exitJson = [];
fs.readdir(movieFolder, function (err, files) {
var sendError = (err) => res.status(500).send(err.message);
if (err)
return sendError(err);
function loadFile (i) {
if (i == files.length)
return res.status(200).json(exitJson); // !!!DONE!!!
var file = files[i];
fs.createReadStream(movieFolder + file)
.pipe(new PNG({filterType: 1}))
.on('parsed', function () {
console.log('Parsing: ' + movieFolder + file);
exitJson.push({width: this.width, height: this.height, data: []});
loadFile (i + 1); // go to next file
})
.on('error', sendError);
}
loadFile(0); // start recursion
});
});
const exports={};const sizes={'foo.png':[100,200],'bar.png':[200,200],'baz.png':[300,200]};Promise.delay = (t) => new Promise(r => setTimeout(r, t)); const randomTime = (a = 500, b = 1500) => Math.floor(Math.random() * b) + a;
const require=src=>({'fs':{readdir:(d,c)=>{Promise.delay(randomTime()).then(() => c(null,['foo.png','bar.png','baz.png']))},createReadStream:(path)=>({pipe:(f)=>({on:(e,c)=>{const s=sizes[path.split('/').slice(-1)[0]];const a={width:s[0],height:s[1]};a.c=c;Promise.delay(randomTime()).then(() => a.c())}})})},'pngjs':{PNG:class PNG{constructor(a){}}},'firebase-functions':{https:{onRequest:(handler)=>{handler({query:({id:2})},{status:(s)=>({json:(a) => document.getElementById('res').innerHTML = '<pre><code>${JSON.stringify(a, null, 4)}</code></pre>'})})}}}})[src];
// ------------------- ignore the above
const fs = require('fs');
const PNG = require('pngjs').PNG;
const functions = require('firebase-functions');
/**
* Using a new Promise, we can perform multiple async tasks all contained
* within that one Promise which can be resolved or rejected. We read the
* folder directory for its files and pass it on to our Promised 'readFiles'.
*/
function readMovieFiles(folder) { console.log('readMovieFiles', folder)
return new Promise((res, rej) => {
fs.readdir(folder, (err, files) => {
readFiles(files, folder).then(res).catch(rej)
});
});
}
/**
* Given an array of file names within a folder, we can chain together the
* file promises using the reduce method. Starting at an initial value of
* Promise<[]>, each file in the array will be read sequentially.
*/
function readFiles(files, folder) { console.log('readFiles', folder, files)
return Promise.all(files.map(name => readFile(folder + name)));
}
/**
* We read a file and in the parsed callback, we call the res() and pass it
* the newly constructed array containing the newest file to be parsed.
*/
function readFile(path) { console.log('readFile', path)
return new Promise((res, rej) => {
fs.createReadStream(path)
.pipe(new PNG({ filterType: 1 }))
.on('parsed', function() {
console.log('parsedFile', path)
res({
data: [],
width: this.width,
height: this.height
});
});
});
}
exports.movie = functions.https.onRequest((req, res) => {
console.log('********** START FUNCTION ************');
if (!req.query.id) req.query.id = 1;
readMovieFiles('../movies/${req.query.id}/png/').then(exitJson => {
res.status(200).json(exitJson);
}).catch(error => {
res.status(500).json(error);
});
console.log('************* FINISHED *************');
});
<pre><code id="res"></code></pre>
readFiles
немного сбивает с толку, но, по сути, она конвертирует массив имен файлов в массив обещаний и выполняет их последовательно. Например, если вы хотите, чтобы они запускались одновременно, вы можете просто сказать Promise.all(files.map(name => readFile(folder + name, arr)))
.