Каковы плюсы и минусы fs.createReadStream vs fs.readFile в node.js?

Я обманываю 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 может обеспечить лучший пользовательский интерфейс, если этот файл был чем-то большим, например видео? Похоже, что он может быть менее блочным; Это правда? Есть ли другие плюсы, минусы, оговорки или gotchas, которые мне нужно знать?

Ответ 1

Лучший подход, если вы просто подключите "данные" к "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 устарело - если кодировка не указана, она будет работать только с буферами необработанных данных.

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

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

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

Ответ 2

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

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

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

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

Ответ 3

Если это большой файл, тогда "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 и отслеживайте использование памяти.

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

Ответ 4

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