Создание любой ссылки на процесс Nodejs.argv вызывает ошибки в неожиданном месте (чтение файла)

#javascript #node.js #json #mongodb #arguments

#javascript #node.js #json #mongodb #аргументы

Вопрос:

Я пишу код, который генерирует очень большой объект JSON, сохраняет его в файл, затем загружает файл и вставляет данные в коллекцию Mongo. Я хочу передать строку из командной строки при вызове скрипта, который я использую для установки имени файла, а также имени коллекции. Я называю это так: node --max-old-space-size=8192 data_generator.js foo 1000000 .

Код завершается ошибкой ENOENT: no such file or directory, open 'foo.json' в третьей строке функции gen_collection() , в которой я устанавливаю переменную data . Эта ошибка не появляется, когда файл foo.json уже существует, даже если он пуст. Перед сбоем код успешно создает файл foo.json , но он содержит только пустой массив [] .

Код завершается ошибкой с той же точной ошибкой, когда я включаю любую ссылку на process.argv. Это включает в себя, когда я пытаюсь присвоить любой переменной значение из процесса.массив argv. Код работает, когда я устанавливаю переменные fname as const fname = "foo" и size as const size = 0 . Однако, даже если единственная ссылка, которую я должен обработать.argv находится в консоли.войти, то есть добавление console.log(process.argv[2] в main() , завершается с той же ошибкой, что и выше.

Вот код, который я пытаюсь запустить:

 const { MongoClient } = require("mongodb");
const fs = require('fs');
const bjson = require('big-json');

async function main() {
  const uri = "my db uri";
  const client = new MongoClient(uri);
  const fname = process.argv[2];
  const size = parseInt(process.argv[3]);
  // const fname = 'small'
  // const size = 1

  try {
    await client.connect({ useUnifiedTopology: true });
    await write_data_to_disk(fname, size);
    await gen_collection(client, fname);
  } catch (e) {
    console.error(e);
  } finally {
    await client.close();
  }
};

// generate data as json aray and write to local file
async function write_data_to_disk(fname, size) {
  let arr = [];
  for (let i = 0; i < size; i  ) {
    let doc = gen_document();
    arr.push(doc);
  }
  const strStream = bjson.createStringifyStream({
    body: arr
  })
  let logger = fs.createWriteStream(`${fname}.json`);
  strStream.on('data', (d) => {
    logger.write(d);
  })
};

async function gen_collection(client, fname) {
  let db = client.db('test');
  let collection = db.collection(fname);

  let data = JSON.parse(fs.readFileSync(`${fname}.json`, 'utf8')); // ERROR APPEARS ON THIS LINE
  bulkUpdateOps = [];
  data.forEach((doc) => {
    bulkUpdateOps.push({"insertOne": {"document": doc}});
    if (bulkUpdateOps.length === 1000) {
      collection.bulkWrite(bulkUpdateOps);
      bulkUpdateOps = [];
    }
  })
  if (bulkUpdateOps.length > 0) {
    collection.bulkWrite(bulkUpdateOps);
  }
};

function gen_document() {
  // returns json object
};
 

Ответ №1:

Вы делаете

 await write_data_to_disk(...)
 

но эта функция не возвращает обещание, к которому подключено, когда оно выполнено. Итак, вы пытаетесь прочитать результирующий файл ДО того, как он был создан или до того, как в нем есть допустимое содержимое, и, следовательно ENOENT , ошибка, поскольку файл еще не существует, когда вы пытаетесь прочитать из него в следующей функции.

Потоки записи плохо сочетаются с обещаниями, если вы не обернете их в свое собственное обещание, которое разрешается, когда вы полностью закончите запись в поток и файл будет закрыт.

Кроме того, вы, вероятно, захотите просто .pipe() передать strStream в поток регистратора. Намного проще, и затем вы можете просто отслеживать, когда выполняется эта операция pipe (), чтобы разрешить обещание, которое вы обертываете вокруг этой операции.

Вы можете пообещать write_data_to_disk() так:

 // generate data as json aray and write to local file
function write_data_to_disk(fname, size) {
    return new Promise((resolve, reject) => {
        const arr = [];
        for (let i = 0; i < size; i  ) {
            let doc = gen_document();
            arr.push(doc);
        }
        const strStream = bjson.createStringifyStream({ body: arr });
        const dest = fs.createWriteStream(`${fname}.json`, {emitClose: true});

        // monitor for completion and errors
        dest.on('error', reject).on('close', resolve);
        strStream.on('error', reject);

        // pipe all the content from strStream to the dest writeStream
        strStream.pipe(dest);
    });
}
 

Поскольку это возвращает обещание, которое действительно привязано к моменту выполнения операции записи, вы можете использовать await write_data_to_disk(...) .

Комментарии:

1. Спасибо за ваш ответ! Это определенно похоже на проблему. Я не совсем понимаю, что вы имеете в виду, используя .pipe(), не могли бы вы объяснить, как я мог бы использовать его в этой ситуации?

2. @jpc — Посмотрите, что я добавил к своему ответу.