Node.js
스트림 사용
수색…
매개 변수
매개 변수 | 정의 |
---|---|
읽을 수있는 스트림 | 데이터를 읽을 수있는 스트림 유형 |
쓰기 가능한 스트림 | 데이터를 쓸 수있는 스트림 유형 |
이중 스트림 | 스트림은 읽기 쉽고 쓸 수 있습니다. |
스트림 변환 | 읽기 및 쓰기가 진행되는 동안 데이터를 변환 할 수있는 양방향 스트림 유형 |
스트림이있는 TextFile의 데이터 읽기
노드의 I / O는 비동기식이므로 디스크 및 네트워크와 상호 작용하려면 콜백을 함수에 전달해야합니다. 다음과 같이 디스크에서 파일을 제공하는 코드를 작성해야 할 수도 있습니다.
var http = require('http');
var fs = require('fs');
var server = http.createServer(function (req, res) {
fs.readFile(__dirname + '/data.txt', function (err, data) {
res.end(data);
});
});
server.listen(8000);
이 코드는 작동하지만 부피가 커서 모든 결과에 대해 클라이언트에 결과를 다시 쓰기 전에 전체 data.txt 파일을 메모리에 버퍼링합니다. data.txt가 매우 큰 경우, 특히 느린 연결을 사용하는 사용자의 경우 많은 사용자가 동시에 사용할 수 있기 때문에 프로그램에서 많은 메모리를 먹을 수 있습니다.
사용자가 전체 파일을 서버의 메모리에 버퍼링하여 내용을 받기 시작할 때까지 기다려야하기 때문에 사용자 환경이 좋지 않습니다.
다행히도 (req, res) 인수는 스트림이므로 fs.readFile () 대신 fs.createReadStream ()을 사용하여 훨씬 더 좋은 방법으로이 코드를 작성할 수 있습니다.
var http = require('http');
var fs = require('fs');
var server = http.createServer(function (req, res) {
var stream = fs.createReadStream(__dirname + '/data.txt');
stream.pipe(res);
});
server.listen(8000);
여기 .pipe ()는 fs.createReadStream ()에서 'data'및 'end'이벤트를 수신 대기합니다. 이 코드는 더 깨끗하지만, 이제는 data.txt 파일이 디스크에서 수신 될 때 즉시 한 번에 하나의 청크에 기록됩니다.
파이프 스트림
읽을 수있는 스트림은 쓰기 가능한 스트림에 "파이프"되거나 연결될 수 있습니다. 따라서 많은 노력을하지 않고 소스 스트림에서 대상 스트림으로 데이터를 흐르게합니다.
var fs = require('fs')
var readable = fs.createReadStream('file1.txt')
var writable = fs.createWriteStream('file2.txt')
readable.pipe(writable) // returns writable
쓰기 가능한 스트림이 읽을 수있는 스트림 인 경우 (예 : 양방향 스트림 인 경우) 다른 쓰기 가능한 스트림으로 계속 파이프 연결할 수 있습니다.
var zlib = require('zlib')
fs.createReadStream('style.css')
.pipe(zlib.createGzip()) // The returned object, zlib.Gzip, is a duplex stream.
.pipe(fs.createWriteStream('style.css.gz')
읽을 수있는 스트림을 여러 스트림으로 파이프 할 수도 있습니다.
var readable = fs.createReadStream('source.css')
readable.pipe(zlib.createGzip()).pipe(fs.createWriteStream('output.css.gz'))
readable.pipe(fs.createWriteStream('output.css')
데이터 흐름 전에 동 기적으로 (동시에) 출력 스트림으로 파이프해야합니다. 그렇지 않으면 불완전한 데이터가 스트리밍 될 수 있습니다.
또한 스트림 객체는 error
이벤트를 error
시킬 수 있습니다. 필요에 따라 모든 스트림에서 이러한 이벤트를 책임있게 처리해야합니다.
var readable = fs.createReadStream('file3.txt')
var writable = fs.createWriteStream('file4.txt')
readable.pipe(writable)
readable.on('error', console.error)
writable.on('error', console.error)
독자적인 읽기 / 쓰기 가능 스트림 만들기
우리는 스트림 객체가 fs와 같은 모듈에 의해 반환되는 것을 보게 될 것이지만 우리 자신의 streamable 객체를 만들고 싶다면 어떻게 될 것인가?
Stream 객체를 만들려면 NodeJs에서 제공하는 스트림 모듈을 사용해야합니다.
var fs = require("fs");
var stream = require("stream").Writable;
/*
* Implementing the write function in writable stream class.
* This is the function which will be used when other stream is piped into this
* writable stream.
*/
stream.prototype._write = function(chunk, data){
console.log(data);
}
var customStream = new stream();
fs.createReadStream("am1.js").pipe(customStream);
이렇게하면 우리 자신 만의 쓰기 가능한 스트림을 얻을 수 있습니다. 우리는 _write 함수 내에서 아무 것도 구현할 수 있습니다. 위의 메소드는 NodeJs 4.xx 버전에서 작동하지만 NodeJs 6.x ES6 에서는 클래스가 도입되어 구문이 변경되었습니다. 다음은 NodeJs의 6.x 버전 코드입니다.
const Writable = require('stream').Writable;
class MyWritable extends Writable {
constructor(options) {
super(options);
}
_write(chunk, encoding, callback) {
console.log(chunk);
}
}
왜 스트림?
파일의 내용을 읽는 두 가지 예제를 살펴 보겠습니다.
첫 번째는 파일을 읽는 데 비동기 메서드를 사용하고 파일이 메모리에 완전히 읽혀지면 호출되는 콜백 함수를 제공합니다.
fs.readFile(`${__dirname}/utils.js`, (err, data) => {
if (err) {
handleError(err);
} else {
console.log(data.toString());
}
})
두 번째는 파일 내용을 읽기 위해 streams
을 사용 streams
.
var fileStream = fs.createReadStream(`${__dirname}/file`);
var fileContent = '';
fileStream.on('data', data => {
fileContent += data.toString();
})
fileStream.on('end', () => {
console.log(fileContent);
})
fileStream.on('error', err => {
handleError(err)
})
두 예제 모두 똑같은 것을 언급 할 가치가 있습니다. 그 때의 차이점은 무엇입니까?
- 첫 번째 것은 더 짧고 더 우아 해 보입니다.
- 두 번째는 파일 을 읽는 동안 파일에 대한 일부 처리를 수행 할 수있게 해줍니다 (!).
처리 할 파일이 작 으면 streams
사용할 때 실제 효과가 없지만 파일이 클 때 어떤 일이 발생합니까? (너무 커서 메모리에 읽는 데 10 초가 걸린다)
streams
이 없다면 10 초가 지나고 파일이 완전히 읽힐 때까지 아무것도하지 않으면 서 기다릴 것입니다. (프로세스가 다른 작업을하지 않는 한) 파일 처리가 시작될 수 있습니다.
streams
사용하면 파일 콘텐츠를 사용할 수있을 때 바로 가져올 수 있습니다.이 기능을 사용하면 파일 을 읽는 동안 파일 을 처리 할 수 있습니다.
위의 예에서는 콜백 방식으로 갈 때 수행 할 수없는 작업에 streams
을 활용하는 방법을 보여주지 않으므로 다른 예제를 살펴보십시오.
gzip
파일을 다운로드하고 압축을 풀어 내용을 디스크에 저장하고 싶습니다. 파일의 url
감안할 때 이것이 필요한 것입니다 :
- 파일 다운로드
- 파일의 압축을 풉니 다.
- 디스크에 저장하십시오.
여기 내 S3
저장소에 저장되어있는 [작은 파일] [1]이 있습니다. 다음 코드는 콜백 방식으로 위의 작업을 수행합니다.
var startTime = Date.now()
s3.getObject({Bucket: 'some-bucket', Key: 'tweets.gz'}, (err, data) => {
// here, the whole file was downloaded
zlib.gunzip(data.Body, (err, data) => {
// here, the whole file was unzipped
fs.writeFile(`${__dirname}/tweets.json`, data, err => {
if (err) console.error(err)
// here, the whole file was written to disk
var endTime = Date.now()
console.log(`${endTime - startTime} milliseconds`) // 1339 milliseconds
})
})
})
// 1339 milliseconds
다음은 streams
사용하는 방법입니다.
s3.getObject({Bucket: 'some-bucket', Key: 'tweets.gz'}).createReadStream()
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream(`${__dirname}/tweets.json`));
// 1204 milliseconds
네, 작은 파일을 다룰 때는 더 빠르지 않습니다. 테스트 된 파일의 무게는 80KB
입니다. 더 큰 파일 인 71MB
압축 파일 (압축되지 382MB
) 382MB
파일을 테스트하면 streams
버전이 훨씬 빠릅니다.
- 그것은 다운로드 20,925 밀리 초를했다
71MB
, 압축을 풀고 다음 쓰기382MB
콜백 방식을 사용하여 - 디스크를. - 비교해 보면
streams
버전을 사용할 때도 동일한 작업을 수행하는 데 13434 밀리 초가 걸렸습니다 (크지 않은 파일의 경우 35 % 빨라짐)