NodeJS вопрос по работе с модулем FS
Работаю над небольшим проектом, где стоит задача считать json файлы в папке , на их основе собрать строку и вернуть ее. Казалось бы - просто, но я завис на моменте асинхронности, уже как только не пробовал и менял на Sync методы Ноды, и async/await, но у меня не получается вернуть контент. Функция возвращает значение до того, как все файлы прочитаются и сформируется результирующая строка.
Это текущий вид файлов:
function loadReport(user) {
const data = fs.readFileSync(path.join(__dirname, '../reports', user))
return JSON.parse(data).content
}
//Generate full report using reports files
const fullyReportGenerator = async function(){
var fullReport = ''
var finalRep = await fs.readdir(path.join(__dirname, '../reports'), (err , files)=>{
files.forEach((user) => {
fullReport += loadReport(user)
})
return fullReport // Вот тут в переменной все то, что надо
})
return finalRep; // А вот тут уже undefined
}
Вызываю потом эту функцию в другом файле:
bot.command('/report', (ctx) => {
fullyReportGenerator()
})
Я понимаю, что ответ на поверхности, но я уже немного запутался и прошу совета.
Ответы (1 шт):
Асинхронно на async/await
const { promises: fs } = require('fs');
const path = require('path');
const bot = {
command: (cmd, cb) => cb({ctx: 'ctx'})
};
bot.command('/report', (ctx) => {
fullyReportGenerator()
.then((reportRawData) => {
console.log(reportRawData);
console.log(ctx);
})
.catch(console.error);
});
async function fullyReportGenerator() {
const reportDirectoryPath = path.join(__dirname, '../reports');
const reportFiles = await readDir(reportDirectoryPath);
const reportData = [];
for (const filePath of reportFiles) {
const reportRawDataItem = await readFile(filePath);
if (typeof reportRawDataItem === 'object' && 'content' in reportRawDataItem) {
reportData.push(reportRawDataItem.content);
}
}
return reportData;
}
async function readDir(dirPath) {
const files = await fs.readdir(dirPath);
return files.map((x) => path.join(dirPath, x));
}
async function readFile(filePath) {
const data = await fs.readFile(filePath, {encoding: 'utf-8'});
try {
const json = JSON.parse(data);
return json;
} catch (err) {
console.error(err);
}
return data;
}
Приведённый код является не более чем иллюстрацией подхода к решению задачи или решением для небольшого количества небольших отчётов.
Не учитывает, что в каталоге report могут быть подкаталоги (решается проверками и рекурсивным вызовом readDir).
Чтение файлов происходит последовательно, но при большом количестве файлов нужно распараллелить (я использую p-queue).
Не оптимально с тз производительности, если файлы отчётов большого размера, но можно уйти на стримы.
Полностью синхронный
На небольших количествах файлов и небольших данных будет, скорее всего, работать быстрее.
const fs = require('fs');
const path = require('path');
const bot = {
command: (cmd, cb) => cb({ctx: 'ctx'})
};
bot.command('/report', (ctx) => {
const reportRawData = fullyReportGenerator();
console.log(reportRawData);
console.log(ctx);
});
function fullyReportGenerator() {
const reportDirectoryPath = path.join(__dirname, '../reports');
const reportFiles = readDir(reportDirectoryPath);
const reportData = [];
for (const filePath of reportFiles) {
const reportRawDataItem = readFile(filePath);
if (typeof reportRawDataItem === 'object' && 'content' in reportRawDataItem) {
reportData.push(reportRawDataItem.content);
}
}
return reportData;
}
function readDir(dirPath) {
const files = fs.readdirSync(dirPath);
return files.map((x) => path.join(dirPath, x));
}
function readFile(filePath) {
const data = fs.readFileSync(filePath, {encoding: 'utf-8'});
try {
const json = JSON.parse(data);
return json;
} catch (err) {
console.error(err);
}
return data;
}