Node.js 스트림의 내용을 문자열 변수로 어떻게 읽습니까?


113

smtp-protocolSMTP 이메일을 캡처하고 메일 데이터를 처리 하는 데 사용하는 Node 프로그램을 해킹하고 있습니다. 라이브러리는 메일 데이터를 스트림으로 제공하며 문자열로 가져 오는 방법을 모르겠습니다.

나는 현재 그것을 stdout에 쓰고 stream.pipe(process.stdout, { end: false })있지만 말했듯이 스트림 데이터가 대신 문자열로 필요하며 스트림이 끝나면 사용할 수 있습니다.

Node.js 스트림의 모든 데이터를 문자열로 수집하려면 어떻게해야합니까?


스트림을 복사하거나 (autoClose : false)로 플래그를 지정해야합니다. 기억을 오염시키는 것은 나쁜 습관입니다.
19h

답변:


41

(이 답변은 몇 년 전 베스트 답변이었던 것입니다. 이제이 아래에 더 나은 답변이 있습니다. node.js를 따라 가지 않았으며이 질문에 "정답"이라고 표시되어 있기 때문에이 답변을 삭제할 수 없습니다. ". 다운 클릭을 생각하고 있다면 어떻게 하시겠습니까?)

핵심은 Readable Streamdataend이벤트 를 사용하는 것 입니다. 다음 이벤트를 들어보십시오.

stream.on('data', (chunk) => { ... });
stream.on('end', () => { ... });

data이벤트 를 수신하면 데이터를 수집하기 위해 생성 된 버퍼에 새 데이터 청크를 추가합니다.

end이벤트 를 받으면 필요한 경우 완성 된 버퍼를 문자열로 변환합니다. 그런 다음 필요한 작업을 수행하십시오.


149
API에서 링크를 가리키는 것보다 답변을 보여주는 몇 줄의 코드가 더 좋습니다. 대답에 동의하지 말고 충분히 완전하다고 믿지 마십시오.
arcseldon

3
최신 node.js 버전에서는 더 깔끔합니다. stackoverflow.com/a/35530615/271961
Simon A. Eugster

약속 라이브러리 사용을 권장하지 않고 기본 약속을 사용하도록 답변을 업데이트해야합니다.
Dan Dascalescu

@DanDascalescu 동의합니다. 문제는 내가이 답변을 7 년 전에 썼고 node.js를 따라 가지 못했다는 것입니다. 다른 사람이 업데이트하고 싶다면 좋을 것입니다. 또는 이미 더 나은 답변이있는 것처럼 보이므로 간단히 삭제할 수 있습니다. 어떤 것을 권 하시죠?
ControlAltDel

@ControlAltDel : 더 이상 최선이 아닌 답변을 삭제 해주셔서 감사합니다. 다른 사람들도 비슷한 규율을 갖기를 바랍니다 .
Dan Dascalescu

129

또 다른 방법은 스트림을 promise로 변환하고 (아래 예제 참조) then(또는 await)을 사용 하여 해결 된 값을 변수에 할당하는 것입니다.

function streamToString (stream) {
  const chunks = []
  return new Promise((resolve, reject) => {
    stream.on('data', chunk => chunks.push(chunk))
    stream.on('error', reject)
    stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
  })
}

const result = await streamToString(stream)

나는 스트림 및 약속을 처음 사용하는데이 오류가 발생 SyntaxError: await is only valid in async function합니다.. 내가 뭘 잘못하고 있죠?
JohnK

비동기 함수 내에서 streamtostring 함수를 호출해야합니다. 이것을 피하기 위해 당신도 할 수 있습니다streamToString(stream).then(function(response){//Do whatever you want with response});
Enclo Creations

23
이것이 최고의 답변이어야합니다. (1) 청크를 버퍼로 저장하고 청크 .toString("utf8")가 멀티 바이트 문자 중간에서 분할되는 경우 디코딩 실패 문제를 방지하기 위해 마지막 에만 호출 하여 모든 것을 올바르게 처리하는 유일한 솔루션을 생성 한 것을 축하합니다 . (2) 실제 오류 처리; (3) 코드를 함수에 넣어서 복사-붙여 넣기가 아닌 재사용 할 수 있도록합니다. (4) Promises를 사용하여 기능을 await활성화 할 수 있습니다 . (5) 특정 npm 라이브러리와 달리 백만 개의 종속성을 끌어 내지 않는 작은 코드; (6) ES6 구문 및 최신 모범 사례.
MultiplyByZer0

청크 배열을 프라 미스로 옮기지 않는 이유는 무엇입니까?
Jenny O'Reilly

1
힌트로 현재 최상위 답변을 사용하여 본질적으로 동일한 코드를 생각 해낸 Uncaught TypeError [ERR_INVALID_ARG_TYPE]: The "list[0]" argument must be an instance of Buffer or Uint8Array. Received type string후 스트림 stringBuffer. 사용 chunks.push(Buffer.from(chunk))stringBuffer청크 모두에서 작동해야 합니다.
Andrei LED

67

위의 어느 것도 나를 위해 일하지 않았습니다. Buffer 개체를 사용해야했습니다.

  const chunks = [];

  readStream.on("data", function (chunk) {
    chunks.push(chunk);
  });

  // Send the buffer or you can put it into a var
  readStream.on("end", function () {
    res.send(Buffer.concat(chunks));
  });

7
이것은 실제로 그 일의 가장 깨끗한 방법입니다)
이보

7
잘 작동합니다. 그냥 참고 : 적절한 문자열 유형을 원한다면, 당신은) (CONCAT에서 결과 버퍼 객체로 .toString ()를 호출하는 호출해야합니다
브라이언 존슨

64

위의 답변보다 유용하기를 바랍니다.

var string = '';
stream.on('data',function(data){
  string += data.toString();
  console.log('stream data ' + part);
});

stream.on('end',function(){
  console.log('final output ' + string);
});

문자열 연결은 문자열 부분을 수집하는 가장 효율적인 방법은 아니지만 단순성을 위해 사용됩니다 (그리고 코드가 효율성에 관심이 없을 수도 있음).

또한이 코드는 비 ASCII 텍스트에 대해 예측할 수없는 오류를 생성 할 수 있지만 (모든 문자가 한 바이트에 맞는다고 가정 함) 아마도 그것에 대해 신경 쓰지 않을 것입니다.


4
스트링 부품을 수집하는 더 효율적인 방법은 무엇입니까? TY
sean2078 2015 년

2
docs.nodejitsu.com/articles/advanced/buffers/how-to-use-buffers 버퍼를 사용할 수 있지만 실제로는 사용에 따라 다릅니다.
Tom Carchrae 2015-08-27

2
각 새 청크를 join("")배열에 추가하고 끝에 배열을 호출 하는 문자열 배열을 사용하십시오 .
Valeriu Paloş

14
이것은 옳지 않습니다. 버퍼가 멀티 바이트 코드 포인트의 중간에있는 경우 toString ()은 잘못된 형식의 utf-8을 수신하고 문자열에 가 표시됩니다.
alextgordon

2
@alextgordon이 맞습니다. 아주 드문 경우에 청크가 많았을 때 청크의 시작과 끝 부분에 그런 것을 얻었습니다. 특히 가장자리에 러시아 기호가있는 경우. 따라서 청크를 변환하고 연결하는 대신 청크를 연결하고 끝에서 변환하는 것이 옳습니다. 제 경우에는 기본 인코딩을 사용하는 request.js를 사용하여 한 서비스에서 다른 서비스로 요청이 이루어졌습니다
Mike Yermolayev

21

일반적으로이 간단한 함수를 사용하여 스트림을 문자열로 변환합니다.

function streamToString(stream, cb) {
  const chunks = [];
  stream.on('data', (chunk) => {
    chunks.push(chunk.toString());
  });
  stream.on('end', () => {
    cb(chunks.join(''));
  });
}

사용 예 :

let stream = fs.createReadStream('./myFile.foo');
streamToString(stream, (data) => {
  console.log(data);  // data is now my string variable
});

1
:이 배열에 밀려 전에 각 청크가 문자열로 변환해야합니다 같은 유용한 대답은하지만 보인다chunks.push(chunk.toString());
니콜라스 르 티에리 드 Ennequin을

1
이것은 나를 위해 일한 유일한 사람입니다! 큰 감사
538ROMEO

1
이것은 훌륭한 대답이었습니다!
Aft3rL1f3

12

그리고 promise를 사용하는 문자열에 대한 또 다른 하나 :

function getStream(stream) {
  return new Promise(resolve => {
    const chunks = [];

    # Buffer.from is required if chunk is a String, see comments
    stream.on("data", chunk => chunks.push(Buffer.from(chunk)));
    stream.on("end", () => resolve(Buffer.concat(chunks).toString()));
  });
}

용법:

const stream = fs.createReadStream(__filename);
getStream(stream).then(r=>console.log(r));

.toString()필요한 경우 바이너리 데이터와 함께 사용할을 제거하십시오 .

update : @AndreiLED가 문자열에 문제가 있음을 올바르게 지적했습니다. 내가 가지고있는 노드 버전으로 문자열을 반환하는 스트림을 얻을 수 없었지만 API 는 이것이 가능하다는 것을 알립니다 .


위의 코드 Uncaught TypeError [ERR_INVALID_ARG_TYPE]: The "list[0]" argument must be an instance of Buffer or Uint8Array. Received type string는 스트림 stringBuffer. 사용 chunks.push(Buffer.from(chunk))stringBuffer청크 모두에서 작동해야 합니다.
Andrei LED

좋은 점, 답변을 업데이트했습니다. 감사.
estani

8

nodejs 문서 에서이 작업을 수행해야합니다. 인코딩이 단지 한 무리의 바이트라는 것을 알지 못한 채 문자열을 항상 기억하십시오.

var readable = getReadableStreamSomehow();
readable.setEncoding('utf8');
readable.on('data', function(chunk) {
  assert.equal(typeof chunk, 'string');
  console.log('got %d characters of string data', chunk.length);
})

6

스트림에는 .toString()(내가 이해하는) 간단한 기능도없고.toStringAsync(cb) 하지 못하는) 기능 없습니다.

그래서 나만의 도우미 함수를 만들었습니다.

var streamToString = function(stream, callback) {
  var str = '';
  stream.on('data', function(chunk) {
    str += chunk;
  });
  stream.on('end', function() {
    callback(str);
  });
}

// how to use:
streamToString(myStream, function(myStr) {
  console.log(myStr);
});

4

나는 그렇게 사용하여 더 많은 행운을 얻었습니다.

let string = '';
readstream
    .on('data', (buf) => string += buf.toString())
    .on('end', () => console.log(string));

나는 노드를 사용 v9.11.1하고는 readstreama로부터 반응이다 http.get콜백.


3

가장 깨끗한 해결책은 "string-stream"패키지를 사용하는 것입니다.이 패키지는 프라 미스가있는 문자열로 스트림을 변환합니다.

const streamString = require('stream-string')

streamString(myStream).then(string_variable => {
    // myStream was converted to a string, and that string is stored in string_variable
    console.log(string_variable)

}).catch(err => {
     // myStream emitted an error event (err), so the promise from stream-string was rejected
    throw err
})

3

인기있는 (주간 다운로드 5 백만 회 이상) 가벼운 get-stream 라이브러리를 사용하는 쉬운 방법 :

https://www.npmjs.com/package/get-stream

const fs = require('fs');
const getStream = require('get-stream');

(async () => {
    const stream = fs.createReadStream('unicorn.txt');
    console.log(await getStream(stream)); //output is string
})();

2

스트림 감속기와 같은 것은 어떻습니까?

다음은 ES6 클래스를 사용하는 예제입니다.

var stream = require('stream')

class StreamReducer extends stream.Writable {
  constructor(chunkReducer, initialvalue, cb) {
    super();
    this.reducer = chunkReducer;
    this.accumulator = initialvalue;
    this.cb = cb;
  }
  _write(chunk, enc, next) {
    this.accumulator = this.reducer(this.accumulator, chunk);
    next();
  }
  end() {
    this.cb(null, this.accumulator)
  }
}

// just a test stream
class EmitterStream extends stream.Readable {
  constructor(chunks) {
    super();
    this.chunks = chunks;
  }
  _read() {
    this.chunks.forEach(function (chunk) { 
        this.push(chunk);
    }.bind(this));
    this.push(null);
  }
}

// just transform the strings into buffer as we would get from fs stream or http request stream
(new EmitterStream(
  ["hello ", "world !"]
  .map(function(str) {
     return Buffer.from(str, 'utf8');
  })
)).pipe(new StreamReducer(
  function (acc, v) {
    acc.push(v);
    return acc;
  },
  [],
  function(err, chunks) {
    console.log(Buffer.concat(chunks).toString('utf8'));
  })
);

1

이것은 나를 위해 일했으며 Node v6.7.0 문서를 기반으로합니다 .

let output = '';
stream.on('readable', function() {
    let read = stream.read();
    if (read !== null) {
        // New stream data is available
        output += read.toString();
    } else {
        // Stream is now finished when read is null.
        // You can callback here e.g.:
        callback(null, output);
    }
});

stream.on('error', function(err) {
  callback(err, null);
})

1

setEncoding ( 'utf8');

위에서 Sebastian J를 잘했습니다.

몇 줄의 테스트 코드로 "버퍼 문제"가 발생했으며 인코딩 정보를 추가하여 해결했습니다. 아래를 참조하십시오.

문제 시연

소프트웨어

// process.stdin.setEncoding('utf8');
process.stdin.on('data', (data) => {
    console.log(typeof(data), data);
});

입력

hello world

산출

object <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64 0d 0a>

솔루션 시연

소프트웨어

process.stdin.setEncoding('utf8'); // <- Activate!
process.stdin.on('data', (data) => {
    console.log(typeof(data), data);
});

입력

hello world

산출

string hello world

1

나열된 모든 답변은 NodeJS의 기본값이 아닌 흐름 모드에서 읽기 가능한 스트림을 여는 것으로 보이며 일시 중지 된 읽기 가능한 스트림 모드에서 NodeJS가 제공하는 역압 지원이 없기 때문에 제한이있을 수 있습니다. 다음은 Just Buffers, Native Stream 및 Native Stream Transforms를 사용한 구현과 Object Mode 지원입니다.

import {Transform} from 'stream';

let buffer =null;    

function objectifyStream() {
    return new Transform({
        objectMode: true,
        transform: function(chunk, encoding, next) {

            if (!buffer) {
                buffer = Buffer.from([...chunk]);
            } else {
                buffer = Buffer.from([...buffer, ...chunk]);
            }
            next(null, buffer);
        }
    });
}

process.stdin.pipe(objectifyStream()).process.stdout

1

이것에 대해 어떻게 생각하십니까?

// lets a ReadableStream under stream variable 
const chunks = [];

for await (let chunk of stream) {
    chunks.push(chunk)
}

const buffer  = Buffer.concat(chunks);
const str = buffer.toString("utf-8")

작동, 매우 깨끗하고 종속성이 없습니다.
ViRuSTriNiTy

0

프로젝트 종속성에 이미 포함되어 있는 매우 인기있는 stream-buffers패키지 를 사용하면 매우 간단합니다.

// imports
const { WritableStreamBuffer } = require('stream-buffers');
const { promisify } = require('util');
const { createReadStream } = require('fs');
const pipeline = promisify(require('stream').pipeline);

// sample stream
let stream = createReadStream('/etc/hosts');

// pipeline the stream into a buffer, and print the contents when done
let buf = new WritableStreamBuffer();
pipeline(stream, buf).then(() => console.log(buf.getContents().toString()));

0

필자의 경우 콘텐츠 유형 응답 헤더는 Content-Type : text / plain 입니다. 그래서 다음과 같이 Buffer에서 데이터를 읽었습니다.

let data = [];
stream.on('data', (chunk) => {
 console.log(Buffer.from(chunk).toString())
 data.push(Buffer.from(chunk).toString())
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.