Добавить 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 ответа

Если файл всегда отформатирован так, как вы сказали выше, вам нужно сделать три вещи:

  1. Узнайте текущую длину файла и вычтите 2 символа (\n], так как нет конца строки в конце),
  2. Удалите первый символ JSON, который вы сохраняете,
  3. Сохраните файл, используя r+ режим и с start
  4. Конец потока после каждого сохранения.

Вот ссылка на параметры 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"
  }
]
*/
Другие вопросы по тегам