Node.js - File System(buffer,stream)

ryan·2022년 5월 7일
0
post-custom-banner

Buffer / Stream

  • buffer

    • 일정한 크기로 모아두는 데이터, 일정한 크기가 되면 한 번에 처리
    • 버퍼링 : 버퍼에 데이터가 찰 때까지 모으는 작업
  • stream :

    • 데이터의 흐름, 데이터를 일정한 크기로 나눠서 여러 번에 걸쳐서 처리
    • 버퍼(또는 청크)의 크기를 작게 막들어서 주기적으로 데이터를 전달
    • 스트리밍 : 일정한 크기의 데이터를 지속적으로 전달하는 작업
    • 대부분 스트림 방식을 사용. 메모리를 적게 차지하면서 효율적으로 데이터를 조작할 수 있음

버퍼

Buffer.js

const buffer = Buffer.from('buffer buffer buffer');
console.log(buffer); // <Buffer 62 75 66 66 65 72 20 62 75 66 66 65 72 20 62 75 66 66 65 72>
console.log(buffer.length); // 20
console.log(buffer.toString()); // buffer buffer buffer

// 버퍼를 배열로 나누어서 concat으로 합칠 수 있음.
const array = [Buffer.from('this '), Buffer.from('is '), Buffer.from('phrase ')];
console.log(Buffer.concat(array).toString()); // this is phrase

// 아무 값이 없는 5byte 버퍼를 생성하는 것도 가능
console.log(Buffer.alloc(5)); // <Buffer 00 00 00 00 00>

Stream

Stream으로 파일 읽기

const fs = require('fs');
const readStream = fs.createReadStream('./lorem.txt'); 
// fs.createReadStream으로 lorem.txt파일을 가져오는 과정을 readStream 변수에 할당

const data = [];
readStream.on('data', (chunk) => {
  data.push(chunk);
  // 나눠진 chunk를 받을 때마다 배열로 넣어주기
  console.log('data', chunk, chunk.length);
});
readStream.on('end', () => {
  console.log('end', Buffer.concat(data).toString());
  // read가 끝나면 배열에 들어간 data를 concat으로 합치기
});
readStream.on('error', (err) => {
  console.log('err', err);
});
  • stream은 효율성도 있지만 안정성에도 좋다. 고용량 파일에 버퍼방식을 적용한다면 서버 메모리도 고용량에 상응하는 용량이 준비되어야 하고 메모리가 견디지 못하면 서버가 터지진다.
  • stream은 데이터를 잘게 나눠서 처리할 수 있기 때문에 적은 메모리로도 고용량의 파일을 읽고 생성할 수 있기 때문에 메모리를 효율적으로 사용할 수 있다.
  • createReadStream이 한번에 읽는 데이터 chunk는 64kb이다.

Stream으로 파일 읽기

const fs = require('fs');
const writeStream = fs.createWriteStream('./lorem.txt');
writeStream.on('finish', () => {
  console.log('파일 쓰기 완료');
});
writeStream.write('이 글을 씁니다.\n'); // 하나가 하나의 버퍼라고 생각하면 됨
writeStream.write('good good.\n'); 
writeStream.end(); //write 종료

pipe

const fs = require('fs');

const readStream = fs.createReadStream('./readme.txt', {highWaterMark: 16}); 
// 16바이트씩 파일 읽기
const writeStream = fs.createWriteStream('./lorem.txt'); //
readStream.pipe(writeStream); 
// 읽어서 받은 것을 다시 16바이트씩 lorem.txt에 쓰게됨 (파일복사)
  • 파이프를 통해 흐르는 물을 연결할 수 있는 것처럼 스트림끼리 파이프를 연결할 수 있음
  • highWaterMark를 통해 한번에 처리하는 데이터 용량을 설정할 수 있다.
응용예제
const fs = require('fs');
const zlib = require('zlib');
const readStream = fs.createReadStream('./readme.txt', {highWaterMark: 16});
const zlibStream = zlib.createGzip();
const writeStream = fs.createWriteStream('./lorem.txt'); 
readStream.pipe(zlibStream).pipe(writeStream); // 파이프를 연결
  • 파일을 읽고 압축하고 압축한 것을 생성한다.

고용량 파일을 통한 메모리 사용량 비교

bigfile.js
const fs = require('fs');
const file = fs.createWriteStream('./big.txt');

for (let i = 0; i <= 10_000_000; i++) {
  file.write('create file!');
}

file.end();
  • 대략 1기가 용량의 파일을 생성
usememory.js
const fs = require('fs');

// 버퍼 방식 
console.log('before', process.memoryUsage().rss); // 약 19메가바이트
const data1 = fs.readFileSync('./big.txt');
fs.writeFileSync('./big.txt', data1);
console.log('buffer', process.memoryUsage().rss); // 약 1기가바이트

// 스트림 방식
console.log('before', process.memoryUsage().rss);  // 약 19메가 바이트
const readStream = fs.createReadStream('./big.txt'); 
const writeStream = fs.createWriteStream('./big3.txt');
console.log('buffer', process.memoryUsage().rss); // 약 31메가 바이트
  • 버퍼 방식
    • 고용량 파일을 읽고 쓰는데 파일 사이즈만큼의 메모리가 사용되는 것을 확인할 수 있다.
  • 스트림 방식
    • 1gb 바이트 옮기는데 12mb만을 사용하는 것을 확인할 수 있다. (효율성, 동시성 상승)
profile
프론트엔드 개발자
post-custom-banner

0개의 댓글