Які плюси та мінуси fs.createReadStream проти fs.readFile в node.js?


74

Я роздушую про 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 може забезпечити кращу взаємодію з користувачем, якщо відповідний файл був чимось великим, наприклад, відео? Відчувається, що це може бути менш блочно; це правда? Чи є інші плюси, мінуси, застереження чи проблеми, які мені потрібно знати?

Відповіді:


59

Кращий підхід, якщо ви просто збираєтесь підключити "дані" до "write ()" і "закрити" до "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. Хеш inode та час створення із виклику stat () у заголовок ETag. Якщо ви отримали заголовок запиту з відповідним заголовком "if-none-match", надішліть назад 304 Not Modified.
  3. Перевірте if-modified-sinceзаголовок щодо mtimeдати на об’єкті stat. 304, якщо він не був змінений з вказаної дати.

Крім того, загалом, якщо можете, надішліть Content-Lengthзаголовок. (Ви stat-ing файл, так що ви повинні мати це.)


@isaacs, не могли б ви навести приклад того, як ці 3 кроки могли бути реалізовані, дякую!
Євген Кузьменко

1
bufferSizeВаріант застарів на користь highWaterMark.
Umair Ishaq

3
Як це взагалі відповідає на початкове запитання?
CapturedTree

45

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

Клієнт також почне отримувати дані швидше, fs.createReadStreamоскільки вони розсилаються шматками під час читання, тоді як, як fs.readFileзчитує весь файл, і лише потім почне надсилати їх клієнту. Це може бути незначним, але може змінити ситуацію, якщо файл дуже великий, а диски повільні.

Подумайте над цим, однак, якщо ви запустите ці дві функції на файлі розміром 100 МБ, перша завантажить файл на 100 МБ, тоді як остання використовуватиме не більше 4 КБ.

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


Це означає, що fs.readFileми не можемо вловити прогрес на прикладі?
Елементо0

4

Якщо це великий файл, тоді "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 !!");
});

Фактично, ви не побачите "зроблено !!" повідомлення. "readFile" не зможе прочитати вміст файлу, оскільки буфер недостатньо великий, щоб вмістити вміст файлу.

Тепер замість "readFile" використовуйте readStream і стежте за використанням пам'яті.

Примітка: код взято з курсу Samer buna Node на Pluralsight


0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.