Node.js는 본질적으로 비동기식이며 이벤트 중심이므로 I/O 관련 작업을 처리하는 데 이상적입니다. 애플리케이션에서 I/O 관련 작업을 처리하는 경우 Node.js의 스트림을 활용할 수 있습니다. 이제 스트림을 자세히 살펴보고 스트림이 I/O 작업을 단순화하는 방법을 이해해 보겠습니다.
스트림이란 무엇인가요?
스트림은 데이터 소스에서 데이터를 쉽게 읽은 다음 다른 대상으로 전달할 수 있는 Unix 파이프입니다.
간단히 말하면 스트림은 특별한 것이 아니며 단지 몇 가지 메소드를 구현하는 EventEmitter일 뿐입니다. 구현 방법에 따라 스트림은 읽기 가능한 스트림(Readable), 쓰기 가능한 스트림(Writable) 또는 양방향 스트림(Duplex, 동시에 읽기 및 쓰기 가능)이 될 수 있습니다.
읽기 가능한 스트림을 사용하면 데이터 소스에서 데이터를 읽을 수 있고, 쓰기 가능한 스트림을 사용하면 대상에 데이터를 쓸 수 있습니다.
Node.js를 사용해 본 적이 있다면 아마도 흐름을 접했을 것입니다.
예를 들어 Node.js HTTP 서버에서 요청은 읽기 가능한 스트림이고 응답은 쓰기 가능한 스트림입니다.
읽기 및 쓰기 가능한 스트림을 처리하는 데 도움이 되는 fs 모듈을 사용해 보셨을 수도 있습니다.
이제 몇 가지 기본 사항을 알아보고 다양한 유형의 스트림을 이해해 보겠습니다. 이 문서에서는 읽기 가능한 스트림과 쓰기 가능한 스트림에 대해 설명합니다. 양방향 스트림은 이 문서의 범위를 벗어나므로 이에 대해서는 설명하지 않습니다.
읽기 가능한 스트림
읽기 가능한 스트림을 사용하여 데이터 소스에서 데이터를 읽을 수 있습니다. 이 데이터 소스는 시스템의 파일, 메모리의 버퍼, 기타 스트림 등 무엇이든 될 수 있습니다. 스트림은 EventEmitters이기 때문에 다양한 이벤트와 함께 데이터를 보냅니다. 우리는 흐름이 작동하도록 하기 위해 이러한 이벤트를 사용할 것입니다.
스트림에서 데이터 읽기
스트림에서 데이터를 읽는 가장 좋은 방법은 데이터 이벤트를 수신하고 콜백 함수를 추가하는 것입니다. 데이터가 유입되면 읽기 가능한 스트림이 데이터 이벤트를 보내고 콜백 함수가 트리거됩니다. 다음 코드 조각을 살펴보세요.
var fs = require('fs'); var readableStream = fs.createReadStream('file.txt'); var data = ''; var readableStream.on('data', function(chunk){ data += chunk; }); readableStream.on('end', function(){ console.log(data); });
fs.createReadStream은 읽을 수 있는 스트림을 제공합니다.
처음에는 이 스트림이 동적이지 않습니다. 데이터에 대한 이벤트 리스너를 추가하고 콜백 함수를 추가하면 유동적이 됩니다. 그런 다음 작은 데이터 조각을 읽고 이를 콜백 함수에 전달합니다.
스트림 구현자는 데이터 이벤트가 트리거되는 빈도를 결정합니다. 예를 들어 HTTP 요청은 몇 KB의 데이터를 읽을 때 데이터 이벤트를 트리거합니다. 파일에서 데이터를 읽을 때 행을 읽을 때 데이터 이벤트를 발생시키도록 결정할 수 있습니다.
읽을 데이터가 없을 때(파일의 끝을 읽을 때) 스트림은 종료 이벤트를 보냅니다. 위의 예에서는 이 이벤트를 수신하고 파일 읽기가 끝나면 데이터를 인쇄했습니다.
스트림을 읽는 또 다른 방법이 있습니다. 파일 끝을 읽기 전에 스트림 인스턴스에서 read() 메서드를 계속 호출하면 됩니다.
var fs = require('fs'); var readableStream = fs.createReadStream('file.txt'); var data = ''; var chunk; readableStream.on('readable', function(){ while ((chunk = readableStream.read()) != null) { data += chunk; } }); readableStream.on('end', function(){ console.log(data); });
read() 메서드는 내부 버퍼에서 데이터를 읽습니다. 읽을 데이터가 없으면 null 을 반환합니다.
그래서 while 루프에서 read()가 null을 반환하는지 확인하고, null이 반환되면 루프를 종료합니다.
스트림에서 데이터를 읽을 수 있으면 읽기 가능 이벤트가 트리거된다는 점에 유의하세요.
인코딩 설정
기본적으로 스트림에서 읽는 내용은 Buffer 개체입니다. 문자열을 읽으려는 경우에는 적합하지 않습니다. 따라서 다음 예제와 같이 Readable.setEncoding()을 호출하여 스트림 인코딩을 설정할 수 있습니다.
var fs = require('fs'); var readableStream = fs.createReadStream('file.txt'); var data = ''; readableStream.setEncoding('utf8'); readableStream.on('data', function(chunk){ data += chunk; }); readableStream.on('end', function(){ console.log(data); });
위 예제에서는 스트림 인코딩을 utf8로 설정하고 데이터 utf8로 구문 분석되고 콜백 함수의 청크는 문자열이 됩니다.
파이핑
파이핑은 스트림 상태를 직접 관리할 필요 없이 데이터 소스에서 데이터를 읽고 대상에 쓸 수 있는 훌륭한 메커니즘입니다. 먼저 다음 예제를 살펴보겠습니다.
var fs = require('fs'); var readableStream = fs.createReadStream('file1.txt'); var writableStream = fs.createWriteStream('file2.txt'); readableStream.pipe(writableStream);
위 예제에서는 파이프() 메서드를 사용하여 file1의 내용을 file2에 씁니다. Pipe()가 데이터 흐름을 관리하므로 데이터 흐름 속도에 대해 걱정할 필요가 없습니다. 이는 Pipe()를 매우 간결하고 사용하기 쉽게 만듭니다.
pipe()는 대상 스트림을 반환하므로 여러 스트림을 쉽게 연결할 수 있다는 점에 유의하세요!
체인화
아카이브가 있고 압축을 풀고 싶다고 가정해 보세요. 이 작업을 수행하는 방법에는 여러 가지가 있습니다. 하지만 가장 간단한 방법은 파이프와 링크를 사용하는 것입니다.
var fs = require('fs'); var zlib = require('zlib'); fs.createReadStream('input.txt.gz') .pipe(zlib.createGunzip()) .pipe(fs.createWriteStream('output.txt'));
먼저 input.txt.gz를 통해 읽을 수 있는 스트림을 만든 다음 zlib.createGunzip() 스트림을 흐르게 하여 압축을 푼다. 콘텐츠. 마지막으로, 압축이 풀린 내용을 다른 파일에 쓰기 위해 쓰기 가능한 스트림을 추가합니다.
기타 방법
읽기 가능한 스트림에 대한 몇 가지 중요한 개념을 논의했으며, 다음은 알아야 할 몇 가지 방법입니다.
1.Readable.pause() – 이 방법은 스트림의 흐름을 일시 중지합니다. 즉, 더 이상 데이터 이벤트를 트리거하지 않습니다.
2.Readable.resume() – 이 메서드는 위와 반대이며 일시 중지된 스트림을 다시 시작합니다.
3.Readable.unpipe() – 이 메서드는 대상을 제거합니다. 인수가 전달되면 특정 대상에서 읽을 수 있는 스트림이 중지되고, 그렇지 않으면 모든 대상이 제거됩니다.
可写流 (Writable Streams)
可写流让你把数据写入目的地。就像可读流那样,这些也是 EventEmitter ,它们也会触发不同的事件。我们来看看可写流中会触发的事件和方法吧。
写入流
要把数据写如到可写流中,你需要在可写流实例中调用 write() 方法,看看下面的例子:
var fs = require('fs'); var readableStream = fs.createReadStream('file1.txt'); var writableStream = fs.createWriteStream('file2.txt'); readableStream.setEncoding('utf8'); readableStream.on('data', function(chunk){ writableStream.write('chunk'); });
上面的代码非常简单,它只是从输入流中读取数据,然后用 write() 写入到目的地中。
这个方法返回一个布尔值来表示写入是否成功。如果返回的是 true 那表示写入成功,你可以继续写入更多的数据。 如果是 false ,那意味着发生了什么错误,你现在不能继续写入了。可写流会触发一个 drain 事件来告诉你你可以继续写入数据。
写完数据后
当你不需要在写入数据的时候,你可以调用 end() 方法来告诉流你已经完成写入了。假设 res 是一个 HTTP response 对象,你通常会发送响应给浏览器:
res.write('Some Data!!');
res.end();
当 end() 被调用时,所有数据会被写入,然后流会触发一个 finish 事件。注意在调用 end() 之后,你就不能再往可写流中写入数据了。例如下面的代码就会报错:
res.write('Some Data!!');
res.end();
res.write('Trying to write again'); //Error !
这里有一些和可写流相关的重要事件:
1.error – 在写入或链接发生错误时触发
2.pipe – 当可读流链接到可写流时,这个事件会触发
3.unpipe – 在可读流调用 unpipe 时会触发
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持PHP中文网。
更多Node.js Streams文件读写操作详解相关文章请关注PHP中文网!