[Node] 스트림(Stream)

스트림(Stream)

스트림은 배열이나 문자열같은 데이터 컬렉션이다. 스트림은 대용량의 파일을 다룰 때나, 외부 소스로부터 데이터를 한번에 일부분씩 가져올때 사용된다.

스트림이 왜 필요한데?

fs 모듈을 사용하면 파일을 읽고 쓸 수 있다. 그러나 파일이 대용량일 경우, 파일 전체를 모두 로드하기 전에 메모리 버퍼를 절약하기 위해 뭔가를 해야할 때가 있다. 그래서 파일의 일부분씩만 가져올 때 Stream이 필요하다.


읽기 스트림 : createReadStream

읽기 스트림은 data, end, error이벤트를 가진 EventEmitter이다.

종류 설명
data 파일의 일부를 리턴한다.
end 읽기가 완료되었을 때 호출된다.
error 에러가 발생했을 때 호출된다.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var fs = require('fs'); //fs 모듈
var stream = fs.createReadStream('./bigfile');

stream.on('data', function(data) {
console.log('loaded part of the file');
});

stream.on('end', function() {
console.log('all parts is loaded');
});

stream.on('error', function(err) {
console.log('something is wrong');
});


쓰기 스트림 : createWriteStream

인터넷에서 파일을 요청하고 데이터의 일부를 받을 때마다 파일에 쓰는 예제를 살펴보자.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var fs = require('fs');
var request = require('request');

var stream = request('http://i.imgur.com/dmetFjf.jpg');
var writeStream = fs.createWriteStream('test.jpg');

stream.on('data', function(data) {
writeStream.write(data);
});

stream.on('end', function() {
writeStream.end();
});

stream.on('error', function(err) {
console.log('something is wrong');
writeStream.close();
});


pipe

pipe는 입력을 출력으로 리다이렉트 할 수 있게 해주는 또 다른 개념이다. 위의 쓰기 스트림 예제를 아래와 같이 pipe로 표현할 수 있다.

Example

1
2
3
4
5
6
7
var fs = require('fs');
var request = require('request');

var stream = request('http://i.imgur.com/dmetFjf.jpg');
var writeStream = fs.createWriteStream('test.jpg');

stream.pipe(writeStream);

pipe는 stream간에 read와 write event들을 연결해주기 때문에 여러 개의 pipe를 서로 연결할 수도 있다.

Example

1
2
3
4
5
6
7
8
9
var fs = require('fs');
var request = require('request');
var gzip = require('zlib').createGzip();

var stream = request('http://i.imgur.com/dmetFjf.jpg');
var writeStream = fs.createWriteStream('test.jpg');

//write gzipped image file
stream.pipe(gzip).pipe(writeStream);


Readable 스트림

Readable Stream은 데이터를 읽을 때 읽는 타이밍이나 한 번에 얼마나 읽을지를 제어할 수 있게 해준다.

다음은 Readable 스트림 객체가 제공하는 함수이다.

함수 설명
read([size]) 스트림에서 String이나 Buffer, null형태의 데이터를 읽음. size전달 인자를 지정한 경우 읽는 데이터의 크기가 제한됨.
setEncoding(encoding) read()요청 결과를 String형태로 반환 시 인코딩 형태.
pause() 객체에서 생성되는 data이벤트를 중지.
resume() 객체에서 생성되는 data이벤트를 재개.
pipe(destination, [options] 출력 스트림을 destination 필드에 지정된 Writable 스트림 객체에 연결. {end:true}는 Readable이 끝나는 시점에 Writable 목적 스트림도 끝나는 것을 의미.
unpipe([destination]) Writable 목적 스트림에서 객체 제거

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
var fs = require('fs');
var stream = fs.createReadStream('test1.jpg');
var writeStream = fs.createWriteStream('test2.jpg');

stream.on('readable', fuction() {
//stream이 읽을 준비가 됨
var data = stream.read();
writeStream.write(data);
});

stream.on('end', function() {
writeStream.end();
});

stream.read()로 데이터를 읽는 것을 제어할 수 있다. 만약 데이터를 읽을 수 없다면, readable 이벤트는 다시 이벤트 루프에 던져지고 나중에 다시 걸리게 될 것이다.


Writable 스트림

Writable Stream은 새로운 drain이벤트가 추가되었는데, 이 이벤트는 buffer에 있는 모든 데이터가 쓰여졌을 때 걸리게 된다. 이를 통해서 buffer가 비워졌을 때 데이터를 쓸 수 있도록 타이밍을 제어할 수 있다.

다음은 Writable 스트림 객체 상에서 사용 가능한 함수이다.

함수 설명
write(chunk, [encoding], [callback]) 스트림 객체의 데이터 위치에 데이터 청크를 쓴다. 데이터는 String이나 Buffer형태다. encoding이 지정되면 문자열 데이터의 인코딩 정보로 사용된다. callback이 지정되면 데이터가 비워진 이후 호출된다.
end(chunk, [encoding], [callback]) 데이터를 더 이상 수용하지 않고 finish이벤트를 보내는 것을 제외하면 write()와 동일하다.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var fs = require('fs');

var stream = fs.createReadStream('./input.jpg');
var writeStream = fs.createWriteStream('./output.jpg');

var writable = true;
var doRead = function() {
var data = stream.read();
//만약 writable이 false를 리턴한다면, buffer가 꽉 차있다는 뜻임.
writable = writeStream.write(data);
}

stream.on('readable', function() {
if(writable) {
doRead();
} else {
//streambuffer가 꽉 찼으니 drain 이벤트가 발생할 때까지 대기
writeStream.removeAllListeners('drain');
writeStream.once('drain', doRead);
}
});

stream.on('end', function() {
writeStream.end();
});
Share