Добавить JSON в файл, используя потоки узлов
Исходя из того, что я прочитал здесь, я использую fs.createWriteStream
записать JSON в файл. Я обрабатываю данные порциями по 50. Итак, в начале скрипта я открываю свой strea и затем использую функцию для его передачи вместе с некоторым JSON, который довольно хорошо работает для записи.
const myStream = fs.createWriteStream(
path.join(RESULTS_DIR, `my-file.json`),
{
flags: 'a'
}
)
function appendJsonToFile(stream, jsonToAppend) {
return new Promise((resolve, reject) => {
try {
stream.write(JSON.stringify(jsonToAppend, null, 2)
resolve('STREAM_WRITE_SUCCESS')
} catch (streamError) {
reject('STREAM_WRITE_FAILURE', streamError)
}
})
}
appendJsonToFile(myStream, someJson)
Однако, поскольку каждый фрагмент данных, который будет записан, является массивом объектов, структура, которую я в итоге получу в моем файле, будет выглядеть следующим образом:
[
{
"data": "test data 1",
},
{
"data": "test data 2",
}
][
{
"data": "test data 3",
},
{
"data": "test data 4",
}
]
Как я могу добавить эти фрагменты данных, чтобы результат был правильно отформатирован в формате JSON, а не просто в виде серии массивов?
2 ответа
Если файл всегда отформатирован так, как вы сказали выше, вам нужно сделать три вещи:
- Узнайте текущую длину файла и вычтите 2 символа (
\n]
, так как нет конца строки в конце), - Удалите первый символ JSON, который вы сохраняете,
- Сохраните файл, используя
r+
режим и сstart
- Конец потока после каждого сохранения.
Вот ссылка на параметры createWriteStream.
Другое дело, что пункт 4 делает это довольно неэффективным и ставит под сомнение всю идею о том, следует ли здесь использовать потоковую передачу. Я думаю, что это имеет смысл, но вопрос здесь в том, нужен ли вам файл для чтения между записями - если нет, то вы должны использовать поток преобразования в середине и добавить флэш между файлами и после того, как вся работа будет выполнена (beforeExit
Вы просто заканчиваете поток.
Вы можете сделать это по определению, но я являюсь автором фреймворка под названием scramjet, который делает эти случаи намного проще:
const myStream = new scramjet.DataStream();
const file = path.join(RESULTS_DIR, `my-file.json`)
const start = fs.statSync(file).size - 2;
myStream
.flatten()
.toJSONArray()
.shift(1)
.pipe(fs.createWriteStream(
file,
{flags: 'r+', start}
));
function appendJsonToFile(stream, jsonToAppend) {
return new Promise((resolve, reject) => {
try {
stream.write(jsonToAppend)
resolve('STREAM_WRITE_SUCCESS')
} catch (streamError) {
reject('STREAM_WRITE_FAILURE', streamError)
}
})
}
appendJsonToFile(myStream, someJson)
process.on('beforeExit', myStream.end());
Вы можете использовать это, как описано выше, но если вы предпочитаете работать с простыми потоками узлов, это должно подтолкнуть вас в правильном направлении.
Я разрешил код с помощью обработчика ошибок и FILE NOT FOUND. Решение получено от Михала Карпаки.
const path = require('path');
const fs = require('fs');
const getFolderPath = () => __dirname || process.cwd();
const getFilePath = (fileName) => path.join(getFolderPath(), `${fileName}`);
/**
* @param {string} fileName - Included File Name & its Extension
* @param {Array<*>} arrayData
* @return {Promise<*>}
*/
const writeFileAsync = async (fileName, arrayData) => {
const filePath = getFilePath(fileName);
return new Promise((resolve, reject) => {
try {
const _WritableStream = fs.createWriteStream(filePath, {flags: 'r+', start: fs.statSync(filePath).size - 2});
_WritableStream.write(JSON.stringify(arrayData, null, 2).replace(/\[/, ','), (streamError) => {
return reject(['STREAM_WRITE_FAILURE', streamError]);
});
return resolve('STREAM_WRITE_SUCCESS');
} catch (streamError) {
/** ERROR NOT FOUND SUCH FILE OR DIRECTORY !*/
if (streamError.code === 'ENOENT') {
fs.mkdirSync(getFolderPath(), {recursive: true});
return resolve(fs.writeFileSync(filePath, JSON.stringify(
Array.from({...arrayData, length: arrayData.length}), null, 2
)));
}
/** ERROR OUT OF BOUND TO FILE SIZE RANGE - INVALID START POSITION FOR WRITE STREAM !*/
if (streamError instanceof RangeError) {
console.error(`> [ERR_OUT_OF_RANGE] =>`, streamError);
const _WritableStream = fs.createWriteStream(filePath, {flags: 'r+'});
return resolve(_WritableStream.write(JSON.stringify(arrayData, null, 2), (streamError) => {
return reject(['STREAM_WRITE_FAILURE', streamError]);
}));
}
return reject(['STREAM_WRITE_FAILURE', streamError]);
}
});
};
(() => writeFileAsync('test1.json',
[{
key: "value 1"
}, {
key: "value 2"
}]
))();
/* Output after 1st time run =>
[
{
"key": "value 1"
},
{
"key": "value 2"
}
]
*/
/* Output after 2nd time run =>
[
{
"key": "value 1"
},
{
"key": "value 2"
},
{
"key": "value 1"
},
{
"key": "value 2"
}
]
*/