2014-10-21 2 views
1

Java에서 GzipInputStream처럼 작동하는 javascript 루틴을 만들고 싶습니다. node.js를 사용하지 않고 클라이언트 측에서 매우 큰 gzip 파일을 읽으 려합니다. here 간단한 보이는매우 큰 gzip 파일을 node.js가없는 클라이언트 측에서 읽음

function ab2string(buf) { 
    var str = ""; 
    var ab = new Uint16Array(buf); 
    var abLen = ab.length; 
    var CHUNK_SIZE = Math.pow(2, 16); 
    var offset, len, subab; 
    for (offset = 0; offset < abLen; offset += CHUNK_SIZE) { 
     len = Math.min(CHUNK_SIZE, abLen-offset); 
     subab = ab.subarray(offset, offset+len); 
     str += String.fromCharCode.apply(null, subab); 
    } 
    return str; 
} 
function string2ab(str) { 
    var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char 
    var bufView = new Uint16Array(buf); 
    for (var i=0, strLen=str.length; i<strLen; i++) { 
    bufView[i] = str.charCodeAt(i); 
    } 
    return buf; 
} 
function FileGzipStreamer() { 
    var loopholeReader = new FileReader(); 
    var chunkReader = new FileReader(); 
    var delimiter = "\n".charCodeAt(0); 

    var expectedChunkSize = 500000; // Slice size to read 
    var loopholeSize = 500;   // Slice size to search for line end 

    var file = null; 
    var fileSize; 
    var loopholeStart; 
    var loopholeEnd; 
    var chunkStart; 
    var chunkEnd; 
    var allString; 
    var lines; 
    var thisForClosure = this; 
    var handler; 
    var fulltext=[]; 
    var fulltext2=[]; 
    var fextra=false; 
    var fname=false; 
    var fcomment=false; 
    var fhcrc=false; 
    var counter=0; 
    var counter2=0; 
    var binString=[]; 


    // Reading of loophole ended 
    loopholeReader.onloadend = function(evt) { 
     // Read error 
     if (evt.target.readyState != FileReader.DONE) { 
      handler(null, new Error("Not able to read loophole (start:)")); 
      return; 
     } 
     binString=[]; 
     binString=evt.target.result.split('').map(function(e){return e.charCodeAt(0);}); 
     fulltext=fulltext.concat(binString); 
     var len=fulltext.length; 
     $("#conclusion").append("\n"+"Length="+len+"\n"); 
     var start=0; 
     if (fulltext[0]==31 || fulltext[1]==139) { 
      if (fulltext[2]==8) { 
       start=10; 
       if (Number(fulltext[3]&4)!=4 && Number(fulltext[3]&2)!=2 && Number(fulltext[3]&1)!=1 && Number(fulltext[3]&128)!=128) { 
        if (Number(fulltext[3]&32)==32) { 
         fextra=true; 
        } 
        if (Number(fulltext[3]&16)==16) { 
         fname=true; 
        } 
        if (Number(fulltext[3]&8)==8) { 
         fcomment=true; 
        } 
        if (Number(fulltext[3]&64)==64) { 
         fhcrc=true; 
        } 
       } 
       else { 
        $("#conclusion").append("Gzip file is invalid"); 
       } 
       start=10 
       if (fextra==true) { 
        incrementor=fulltext[start]+256*fulltext[start+1]; 
        start+=incrementor+2; // 2 for xlen 
       } 
       if (fname==true) { 
        start+=1; 
        while(fulltext[start-1]!=0) 
         start+=1 
       } 
       if (fcomment==true) { 
        start+=1 
        while(fulltext[start-1]!=0) 
         start+=1 
       } 
       if (fhcrc==true) { 
        start+=2; 
       } 
       var uncompressed=zip_inflate(ab2string(fulltext.slice(28,len))); 
       var splitline=uncompressed.split("\n"); 
       //$("#conclusion").append(splitline.length+"\n"); 
       var temp=counter; 
       $("#conclusion").append("\n"+"Counter="+counter+", Splitlinelength="+splitline.length+"\n"); 
       var uncompressed2=""; 
       //var test=Math.random(); 
       //$("#conclusion").append(uncompressed); 
       for (var i=temp;i<splitline.length-5; i++) { 
        counter+=1; 
        uncompressed2+=splitline[i]+"\n"; 
        //if (splitline[i].indexOf("\n")!=-1) 
        //$("#conclusion").append(i+"start"+splitline[i]+"end\n"); 
        $("#conclusion").append(splitline[i]); 
        $("#conclusion").append("\n"); 
       } 
       var view = new DataView(string2ab(uncompressed2)); 
       var realLoopholeSize = loopholeEnd - loopholeStart; 
       //$("#conclusion").append("1"+uncompressed+"\n\n\n"); 
       //$("#conclusion").append(realLoopholeSize+'--'+fulltext.length+'x'); 
       for(var i = realLoopholeSize - 1; i >= 0; i--) { 
        if (view.getInt8(i) == delimiter) { 
         chunkEnd = loopholeStart + i + 1; 
         var blob = file.slice(chunkStart, chunkEnd); 
         $("#conclusion").append(chunkStart+'xxz'+chunkEnd+'y'); 
         chunkReader.readAsBinaryString(blob); 
         return; 
        } 
       } 

       // No delimiter found, looking in the next loophole 
       $("#conclusion").append("test"); 
       loopholeStart = loopholeEnd; 
       loopholeEnd = Math.min(loopholeStart + loopholeSize, fileSize); 
       thisForClosure.getNextLine(); 
       //$("#conclusion").append(zip_inflate(String.fromCharCode.apply(null,fulltext.slice(start,len)))); 
      } 
      else { 
       $("#conclusion").append("Unknown compression method!"); 
      } 
     } 
     else{ 
      $("#conclusion").append("Not a gzipped file!"); 
     } 
     //$("#conclusion").append(zip_inflate(String.fromCharCode.apply(null,fulltext))); 
     //fulltext=fulltext.concat(arr2); 
     //var theText=zip_inflate(String.fromCharCode.apply(null,fulltext.slice(start,len))); 
     //$("#conclusion").append("yy"+loopholeEnd+'--'+loopholeStart); 
     // No delimiter found, looking in the next loophole 
     //loopholeStart = loopholeEnd; 
     //loopholeEnd = Math.min(loopholeStart + loopholeSize, fileSize); 

     //thisForClosure.getNextLine(); 
    }; 

    // Reading of chunk ended 
    chunkReader.onloadend = function(evt) { 
     // Read error 
     if (evt.target.readyState != FileReader.DONE) { 
      handler(null, new Error("Not able to read loophole")); 
      return; 
     } 
     var binString2=evt.target.result.split('').map(function(e){return e.charCodeAt(0);}); 
     $("#conclusion").append("text2="+binString+"\n"); 
     fulltext2=fulltext2.concat(binString2); 
     var len2=fulltext2.length; 
     var start2=0; 
     if (fulltext2[0]==31 || fulltext2[1]==139) { 
      if (fulltext2[2]==8) { 
       start2=10; 
       if (Number(fulltext2[3]&4)!=4 && Number(fulltext2[3]&2)!=2 && Number(fulltext2[3]&1)!=1 && Number(fulltext2[3]&128)!=128) { 
        if (Number(fulltext2[3]&32)==32) { 
         fextra=true; 
        } 
        if (Number(fulltext2[3]&16)==16) { 
         fname=true; 
        } 
        if (Number(fulltext2[3]&8)==8) { 
         fcomment=true; 
        } 
        if (Number(fulltext2[3]&64)==64) { 
         fhcrc=true; 
        } 
       } 
       else { 
        $("#conclusion").append("Gzip file is invalid"); 
       } 
       if (fextra==true) { 
        incrementor=fulltext2[start2]+256*fulltext2[start2+1]; 
        start2+=incrementor+2; // 2 for xlen 
       } 
       if (fname==true) { 
        start2+=1; 
        while(fulltext2[start2-1]!=0) 
         start2+=1; 
       } 
       if (fcomment==true) { 
        start2+=1 
        while(fulltext2[start2-1]!=0) 
         start2+=1; 
       } 
       if (fhcrc==true) { 
        start2+=2; 
       } 
      } 
     } 
     //$("#conclusion").append(zip_inflate(String.fromCharCode.apply(null,binString))); 
     //binString=binString.concat(arr2); 
     var theText=zip_inflate(ab2string(fulltext2.slice(start2,len2))); 
     //var temp=counter; 
     //var splitline2=theText.split(/\r?\n/); 
     //var uncompressed3=""; 
     //var test=Math.random(); 
     //for (var i=0;i<splitline2.length; i++) { 
      //uncompressed3+=splitline2[i]+"\n"; 
      //$("#conclusion").append(splitline2[i]); 
     //} 

     //$("#conclusion").append("3"+theText+"\n\n\n"); 
     // Remove last new line in the end of chunk 
     if (lines.length > 0 && lines[lines.length - 1] == "") { 
      lines.pop(); 
     } 
     var temp=0; 
     var lines = theText.split(/\r?\n/); 
     for (var i=temp;i<lines.length; i++) { 
      //counter+=1; 
      //uncompressed2+=splitline[i]+"\n"; 
      //if (splitline[i].indexOf("\n")!=-1) 
      //$("#conclusion").append(i+"start"+splitline[i]+"end\n"); 
      $("#conclusion").append(lines[i]); 
      $("#conclusion").append("\n"); 
     } 
     chunkStart = chunkEnd; 
     chunkEnd = Math.min(chunkStart, fileSize); 
     loopholeStart = Math.min(chunkEnd, fileSize); 
     loopholeEnd = Math.min(loopholeStart + loopholeSize, fileSize); 
     thisForClosure.getNextLine(); 
    }; 


    // Public: open file for reading 
    this.open = function (fileToOpen, linesProcessed) { 
     file = fileToOpen; 
     fileSize = file.size; 
     loopholeStart = 0; 
     loopholeEnd = Math.min(loopholeStart + loopholeSize, fileSize); 
     chunkStart = 0; 
     chunkEnd = 0; 
     lines = null; 
     handler = linesProcessed; 
    }; 

    // Public: start getting new line async 
    this.getNextLine = function() { 
     // File wasn't open 
     if (file == null) {  
      handler(null, new Error("You must open a file first")); 
      return; 
     } 
     // Some lines available 
     if (lines != null) { 
      var linesForClosure = lines; 
      setTimeout(function() { handler(linesForClosure, null) }, 0); 
      lines = null; 
      return; 
     } 
     // End of File 
     if (chunkStart == fileSize) { 
      handler(null, null); 
      return; 
     } 
     // File part bigger than expectedChunkSize is left 
     if (loopholeStart < fileSize) { 
      var blob = file.slice(loopholeStart, loopholeEnd); 
      loopholeReader.readAsBinaryString(blob); 
     } 
     // All file can be read at once 
     else { 
      chunkEnd = fileSize; 
      var blob = file.slice(chunkStart, fileSize); 
      chunkReader.readAsBinaryString(blob); 
     } 
    }; 
}; 

알고리즘 : (헤더를 건너 뛰고 팽창 전화) 압축 된 블록 this 같은 일상

here에서 영감을 (아직 작업) 내 코드입니다. 그러나 gzip 파일이 매우 크기 때문에 (수십 또는 수백 GB) 압축 된 블록을 조각별로 팽창시켜야합니다.

Node.js를 사용하지 않고 Java GzipInputStream과 같이 압축 된 블록을 파티션하고 압축 할 수있는 방법이 있습니까?

+0

나에게 미친 짓처럼 보인다. 브라우저에서 이것을 실행하고 있다면 대부분의 컴퓨터에서 _hours_를 잠글 것입니다. 그리고 파이어 폭스 같은 일부 브라우저는 그렇게 오래 있지 않아도 "응답하지 않는 스크립트"경고를 표시합니다. – GregL

+0

그래서 WebWorker를 사용합니다. 그리고 제 경우에는 gzip 읽기가 완료되면 조건이 충족됩니다 (WebWorker 종료). 그것은 [이] (http://stackoverflow.com/questions/24647563/reading-line-by-line-file-in-javascript-on-client-side)와 같이 압축되지 않은 파일을 읽는 데 효과적입니다. 문제는 gzip을 부분별로 디코드/팽창시키는 방법입니다. – dhany1024

+0

스트림과 함께 사용할 수 있다고 생각합니다. 그것이 그들이하는 일입니다. 스트림이 browserify 프로젝트를 통해 브라우저로 이식되었습니다. https://github.com/substack/stream-browserify – Sukima

답변

2

노드에서 우리는 파일 (fs.createReadableStream())에서 읽을 수있는 스트림을 만들고 zlib.createGunzip()에 파이프 할 수 있습니다. 읽을 수있는 스트림은 청크 단위로 데이터를 읽은 다음 gunzip sink으로 전달합니다. 그러므로 우리가 gzip -ed 파일을이 설정에 넣으면, 우리는 추출 된 데이터를 청크 단위로 가져올 것입니다.

browserify의 도움으로 브라우저에서이 작업을 수행 할 수 있습니다.

// browserify automatically replaces the node's native zlib with this: 
// https://www.npmjs.com/package/browserify-zlib 
var zlib = require('zlib'); 

var drop = require('drag-and-drop-files'); 
var createReadStream = require('filereader-stream'); 

var gunzip = zlib.createGunzip(); 

drop(document.getElementById('drop'), function(files) { 
    var first = files[0]; 
    createReadStream(first).pipe(gunzip); 

    gunzip.on('data', function(data){ 
    // read the data chunk-by-chunk 
    console.log(data.toString()); 
    }); 

    gunzip.on('end', function(){ 
    console.log('done'); 
    }); 
}); 

가이 브라우저 내에서 작동하게하려면이 main.js 파일을 사용하여, 우리는 그것에 browserify 매력을 넣어해야합니다.

$ browserify app.js > bundle.js 

그리고, 우리는합니다 (drop -zone을 잊지 마세요) 그것에 bundle.js와 함께 index.html을로드 할 수 있습니다.

나는이 repo에 poc (스트리밍 부분, 아마도 우리는 매우 큰 파일에 대처하기 위해 webworker API로 게임해야 할 필요가 있습니다)를 모았습니다. JS와 같은 단일 스레드 환경에서 크기의 파일 수백 GB의 읽기 시도

poc

+0

모든 멋진 것들이없는 간단한 방법이 있습니까? 나는'가져 오기 어쩌구; 자바 스크립트에서 새로운 GZIPInputStream (blah blah); – Jus12

관련 문제