Каковы плюсы и минусы fs.createReadStream против fs.readFile в node.js? - PullRequest
70 голосов
/ 04 января 2011

Я копаюсь с node.js и обнаружил два способа чтения файла и отправки его по проводам, как только я установил, что он существует, и отправил правильный тип MIME с writeHead:

// read the entire file into memory and then spit it out

fs.readFile(filename, function(err, data){
  if (err) throw err;
  response.write(data, 'utf8');
  response.end();
});

// read and pass the file as a stream of chunks

fs.createReadStream(filename, {
  'flags': 'r',
  'encoding': 'binary',
  'mode': 0666,
  'bufferSize': 4 * 1024
}).addListener( "data", function(chunk) {
  response.write(chunk, 'binary');
}).addListener( "close",function() {
  response.end();
});

Правильно ли я полагаю, что fs.createReadStream может обеспечить лучшее взаимодействие с пользователем, если рассматриваемый файл был чем-то большим, например видео?Такое ощущение, что это может быть меньше блок-шей;Это правда?Есть ли другие плюсы, минусы, предостережения или ошибки, которые мне нужно знать?

Ответы [ 4 ]

56 голосов
/ 04 января 2011

Лучший подход, если вы просто собираете «data» для «write ()» и «close» для «end ()»:

// 0.3.x style
fs.createReadStream(filename, {
  'bufferSize': 4 * 1024
}).pipe(response)

// 0.2.x style
sys.pump(fs.createReadStream(filename, {
  'bufferSize': 4 * 1024
}), response)

Подход read.pipe(write) или sys.pump(read, write) также имеет преимущество добавления управления потоком. Таким образом, если поток записи не может принимать данные так же быстро, он скажет потоку чтения отойти назад, чтобы минимизировать объем данных, буферизуемых в памяти.

flags:"r" и mode:0666 подразумеваются тем фактом, что это FileReadStream. Кодировка binary устарела - если кодировка не указана, она будет работать только с буферами необработанных данных.

Кроме того, вы можете добавить некоторые другие вкусности, которые сделают ваш файл более привлекательным:

  1. Отнюдь для req.headers.range и посмотри, соответствует ли она строке типа /bytes=([0-9]+)-([0-9]+)/. Если это так, вы хотите просто поток с этого начала до конца места. (Отсутствующее число означает 0 или «конец».)
  2. Хеш инода и время создания из вызова stat () в заголовок ETag. Если вы получили заголовок запроса с "if-none-match", совпадающим с этим заголовком, отправьте обратно 304 Not Modified.
  3. Сравните заголовок if-modified-since с датой mtime объекта статистики. 304, если он не был изменен с указанной даты.

Кроме того, как правило, если можете, отправьте заголовок Content-Length. (Вы stat -е файл, так что вы должны иметь это.)

37 голосов
/ 04 января 2011

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

Клиент также начнет получать данные быстрее, используяfs.createReadStream, поскольку он отсылается порциями по мере чтения, тогда как fs.readFile будет считывать весь файл и только затем начнет отправлять его клиенту.Это может быть незначительным, но может иметь значение, если файл очень большой, а диски медленные.

Подумайте об этом, хотя, если вы запустите эти две функции для файла размером 100 МБ, первая будет использовать 100 МБпамяти, чтобы загрузить файл, в то время как последний будет использовать не более 4 КБ.

Редактировать: Я действительно не вижу причин, по которым вы бы использовали fs.readFile, тем более что вы сказали, что будете открывать большиефайлы.

2 голосов
/ 18 апреля 2017

Если это большой файл, тогда «readFile» перегружает память, поскольку он буферизует все содержимое файла в памяти и может повредить вашу систему Пока ReadStream читается кусками.

Запустите этот код и просмотрите использование памяти на вкладке производительности диспетчера задач.

 var fs = require('fs');

const file = fs.createWriteStream('./big_file');


for(let i=0; i<= 1000000000; i++) {
  file.write('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n');
}

file.end();


//..............
fs.readFile('./big_file', (err, data) => {
  if (err) throw err;
  console.log("done !!");
});

Infact, вы не увидите "готово!" сообщение. «readFile» не сможет прочитать содержимое файла, так как буфер недостаточно велик для хранения содержимого файла.

Теперь вместо readFile используйте readStream и следите за использованием памяти.

Примечание: код взят из курса "Самера буна" по Pluralsight

0 голосов
/ 30 августа 2012

Другая, возможно, не очень известная вещь, это то, что я считаю, что Node лучше очищает неиспользуемую память после использования fs.readFile по сравнению с fs.createReadStream. Вы должны проверить это, чтобы проверить, что работает лучше всего. Кроме того, я знаю, что с каждой новой версией Node это улучшалось (т. Е. Сборщик мусора становился умнее в подобных ситуациях).

...