создатьReadStream в Node.JS

Поэтому я использовал fs.readFile(), и это дало мне

"FATAL ERROR: CALL_AND_RETRY_LAST Не удалось выделить - процессу не хватает памяти"

поскольку fs.readFile() загружает весь файл в память перед вызовом обратного вызова, следует ли вместо этого использовать fs.createReadStream()?

Это то, что я делал ранее с readFile:

fs.readFile('myfile.json', function (err1, data) {
    if (err1) {
        console.error(err1);
    } else {
        var myData = JSON.parse(data);
        //Do some operation on myData here
    }
}

Извините, я новичок в потоковом вещании; следующий правильный способ сделать то же самое, но с потоковой передачей?

var readStream = fs.createReadStream('myfile.json');

readStream.on('end', function () {  
    readStream.close();
    var myData = JSON.parse(readStream);
    //Do some operation on myData here
});

Спасибо


person Community    schedule 02.06.2015    source источник


Ответы (1)


Если файл огромен, то да, потоковая передача — это то, как вы хотите с ним справиться. Однако то, что вы делаете во втором примере, позволяет потоку буферизовать все данные файла в памяти, а затем обрабатывать их на end. В этом смысле он ничем не отличается от readFile.

Вы можете проверить JSONStream. Потоковая передача означает, что вы хотите иметь дело с данными по мере их прохождения. В вашем случае вы, очевидно, должны сделать это, потому что вы не можете сразу буферизовать весь файл в память. Имея это в виду, надеюсь, такой код имеет смысл:

JSONStream.parse('rows.*.doc')

Обратите внимание, что он имеет своего рода шаблон запроса. Это связано с тем, что у вас не будет всего объекта/массива JSON из файла для одновременной работы, поэтому вам нужно больше думать о том, как вы хотите, чтобы JSONStream обрабатывал данные по мере их обнаружения.

Вы можете использовать JSONStream для запроса интересующих вас данных JSON. Таким образом, вы никогда не буферизуете весь файл в памяти. У него есть недостаток: если вам нужны все данные, вам придется передавать файл несколько раз, используя JSONStream для извлечения только тех данных, которые вам нужны в данный момент, но в вашем случае у вас нет большой выбор.

Вы также можете использовать JSONStream для разбора данных по порядку и сделать что-то вроде сброса их в базу данных.

JSONStream.parse похож на JSON.parse, но вместо того, чтобы возвращать весь объект, он возвращает поток. Когда поток синтаксического анализа получает достаточно данных для формирования целого объекта, соответствующего вашему запросу, он выдает событие data с данными, являющимися документом, соответствующим вашему запросу. После того, как вы настроили обработчик данных, вы можете направить поток чтения в поток синтаксического анализа и наблюдать, как происходит волшебство.

Пример:

var JSONStream = require('JSONStream');
var readStream = fs.createReadStream('myfile.json');
var parseStream = JSONStream.parse('rows.*.doc');
parseStream.on('data', function (doc) {
  db.insert(doc); // pseudo-code for inserting doc into a pretend database.
});
readStream.pipe(parseStream);

Это подробный способ помочь вам понять, что происходит. Вот более лаконичный способ:

var JSONStream = require('JSONStream');
fs.createReadStream('myfile.json')
  .pipe(JSONStream.parse('rows.*.doc'))
  .on('data', function (doc) {
    db.insert(doc);
  });

Редактировать:

Для большей ясности в том, что происходит, попробуйте подумать об этом так. Допустим, у вас есть гигантское озеро, и вы хотите обработать воду, чтобы очистить ее и переместить воду в новый резервуар. Если бы у вас был гигантский волшебный вертолет с огромным ведром, вы могли бы пролететь над озером, поместить озеро в ведро, добавить в него химикаты для обработки, а затем доставить его к месту назначения.

Проблема, конечно, в том, что нет такого вертолета, который мог бы справиться с таким большим весом или объемом. Это просто невозможно, но это не значит, что мы не можем достичь своей цели по-другому. Вместо этого вы строите ряд рек (ручьев) между озером и новым водохранилищем. Затем вы устанавливаете очистительные станции в этих реках, которые очищают любую воду, проходящую через них. Эти станции могут работать по-разному. Может быть, лечение можно провести так быстро, что вы позволите реке течь свободно, и очищение произойдет только тогда, когда вода будет течь по течению с максимальной скоростью.

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

Это почти то, что вы хотите сделать с вашими данными. Поток синтаксического анализа — это ваша очищающая станция, и он буферизует данные до тех пор, пока их не будет достаточно для формирования всего документа, соответствующего вашему запросу, затем он отправляет только эти данные вниз по течению (и генерирует событие data).

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

Если бы у вас был драйвер потоковой базы данных, то теоретически вы могли бы создать какой-то поток вставки, а затем выполнить parseStream.pipe(insertStream) вместо того, чтобы вручную обрабатывать событие data :D. Вот пример создания отфильтрованной версии вашего файла JSON в другом файле.

fs.createReadStream('myfile.json')
  .pipe(JSONStream.parse('rows.*.doc'))
  .pipe(JSONStream.stringify())
  .pipe(fs.createWriteStream('filtered-myfile.json'));
person Chev    schedule 02.06.2015
comment
Большое спасибо за Вашу помощь! Значит ли это, что db.insert(doc) вызывается один раз, и тогда у вас есть все данные? Спасибо еще раз - person ; 02.06.2015
comment
Нет, он будет вызываться для каждого документа, соответствующего запросу, который вы отправляете JSONStream.parse. Это событие будет генерироваться для каждого матча. Взгляните на их примеры и представьте, что мы анализируем тот пример большого двоичного объекта JSON, который они используют. Предполагая, что это данные, которые мы анализируем, вы получите ровно два события data. По одному для каждого свойства doc из двух строк в массиве rows. Есть смысл? - person Chev; 02.06.2015
comment
Если ваш файл настолько велик, что вы получаете процесс из памяти, когда пытаетесь загрузить его, то вы должны понимать, что у вас никогда не будет все данные одновременно. Вам нужно будет передавать файл через потоки синтаксического анализа JSONStream всякий раз, когда вам нужно прочитать данные из него, запрашивая только то, что вам нужно в данный момент. Если это проблема, я настоятельно рекомендую разбить этот чудовищный файл JSON;). - person Chev; 02.06.2015
comment
Без проблем. Я добавил небольшую аналогию в нижней части ответа, если это поможет сделать ситуацию еще понятнее. Не забудьте принять ответ, если он вам помог :) - person Chev; 02.06.2015
comment
Заслуживает больше голосов! Хорошее объяснение, особенно с этими реками - person Steven; 10.08.2016