2016-10-07 5 views
0

매우 큰 base64 인코딩 된 문자열을 바이트 (Uint8) 배열로 읽은 다음 해당 바이트 배열을 지정된 크기의 청크로 분할해야합니다. 별도로 청크. 아래 함수를 사용하면 작동하지만 .slice 또는 .toString을 호출하면 각 호출마다 힙의 메모리가 증가합니다. 왜냐하면 (나는) 그것이 버퍼의 복사본을 만들고 있기 때문입니다. 특히 큰 base64Encoded 문자열의 경우 애플리케이션의 힙 공간이 부족합니다. 이것을 특정 크기로 나누고 base64로 메모리가 부족한 상태로 인코딩하려면 어떻게해야할까요?Node.js 메모리가 부족한 매우 큰 버퍼를 자름

const process = function (reallyLargeBase64EncodedString, splitSize){ 

var theBuffer = Buffer.from(reallyLargeBase64EncodedString, 'base64'); 

//var tempBuffer = new Buffer(splitSize); 
for (var start = 0; start < theBuffer.length; start += splitSize) { 
    //for(var z = 0; z < splitSize; z++){ 
     //tempBuffer.writeUInt8(theBuffer[start+z],z); 
    //} 
    //var base64EncodedVal = tempBuffer.toString('base64'); 
    //var base64EncodedVal = theBuffer.buffer.toString('base64', start, start+splitSize); 
    var base64EncodedVal = theBuffer.slice(start,start+splitSize).toString('base64'); 
    //do stuff with the base64 encoded value 
} 

}};

+1

아니요,''slice'가 메모리를 복사하지 않습니다. (https : //nodejs.org/api/buffer.html#buffer_buf_slice_start_end). 'toString' 않습니다. 당신이 문자열로하고있는 "물건"은 정확히 무엇입니까? – Bergi

+0

지금 바로 데이터베이스에 삽입하십시오. 음, 문자열에서 BLOB로 행을 변경하고 바이트 배열을 직접 삽입하는 것이 문제가되는 toString입니다. –

+0

데이터베이스 삽입이 비동기입니까? 한 번에 모든 문자열이 메모리에 생성되지 않도록 순차적으로 만들 수 있습니다. – Bergi

답변

4

노드의 스트리밍 인터페이스를 사용하여 큰 것을 처리하는 것이 좋습니다. base64로 인코딩 된 문자열이 파일 또는 네트워크 요청에서 오는 경우 입력에서 직접 base64-stream과 같은 base64 디코드 스트림으로 파이프 할 수 있습니다.

데이터를 청크하고 각 청크를 다시 인코딩하려면 고유 한 변환 스트림 (입력과 출력 사이에 들어오는 스트림)을 작성해야합니다.

// NOTE: the following code has been tested in node 6. 
// since it relies on the new Buffer api, it must be run in 5.10+ 
var Transform = require('stream').Transform; 

class ChunkEncode extends Transform { 
    constructor(options){ 
     super(options); 
     this.splitSize = options.splitSize; 
     this.buffer = Buffer.alloc(0); 
    } 

    _transform(chunk, encoding, cb){ 
     // chunk is a Buffer; 
     this.buffer = Buffer.concat([this.buffer, chunk]); 
     while (this.buffer.length > this.splitSize){ 
      let chunk = this.buffer.slice(0, this.splitSize); 
      // Encode and write back to the stream. 
      this.push(chunk.toString('base64')) 
      // throw in a newline for visibility. 
      this.push('\n'); 
      // chop off `splitSize` from the start of our buffer. 
      this.buffer = this.buffer.slice(this.splitSize); 
     } 
    } 
} 

등이 보일 것이다 무언가 그럼 당신은 표준 출력에 기록합니다

var fs  = require('fs'); 
var base64 = require('base64-stream'); 

fs.createReadStream('./long-base64-string') 
.pipe(base64.decode()) 
.pipe(new ChunkEncode({splitSize : 128})) 
.pipe(process.stdout) 

같은 것을 할 수 있어야합니다,하지만 당신은 그냥 간단하게 파일이나 네트워크 스트림에 쓸 수 있습니다. 데이터를 추가로 조작해야하는 경우 쓰기 스트림을 만들면 각 데이터 덩어리가 들어올 때마다 무언가를 할 수 있습니다.

+0

이것은 대단한 것이고 나는 이것을 답으로 표시하고 싶다. 그러나 완전히 다른 방식으로이 문제를 해결했으며, 내가 모든 코드를 게시하지 않았기 때문에 당신의 대답이 유일한 이유는 아니다. 미안해. 진짜 문제는 비동기 패러다임에 관해서는 n00b입니다. 이 코드는 쿼리의 결과에서 호출되었으며, 쿼리의 결과는 API를 호출 한 다음 게시 된 코드와 b64 문자열의 삽입을 호출합니다. 초기 데이터베이스 호출은 아직 끝나지 않았으며 내가 처리하고있는 모든 것이 처리 될 때까지 힙에 던져지고있었습니다. –

+0

걱정하지 마십시오. 이와 같은 질문에서 어떤 코드 비트가 중요한지 알기는 어렵습니다. 특히 당신이 질문을 한 이유는 당신이 대답을하지 않았기 때문입니다. 저는이 경우에 절차가 당신 자신의 대답을 쓰는 것이라고 생각합니다. (위의 꽤 많은 주석) 올바른 것으로 표시하십시오. – Mobius

+0

나는 힙 크기를 저장할지 여부를 확인하기 위해 답을 테스트했지만 힙 오버플로를 막을만큼 충분하지 않았지만 barfing 전에 두 번 이상 루프가 발생했습니다. –

관련 문제