Javascriptで巨大なテキストファイルを少しずつ読み込む

  • 2
    Like
  • 0
    Comment

stack overflowのコードをアレンジして実現。

chunk_sizeずつファイルを読み込み、読み込んだファイルを一文字ずつ変数に追加、改行を見つけたら callbackを適用して変数のメモリを解放する。なお、全ての改行コードに対応しているが、出力時には /n に統一される。

残念ながら改行のないテキストデータには使えない。javascriptの変数に格納できる文字数の最大値(Chromeであれば268,435,440文字)を超えた時点でエラーが起きる(その前にタブが落ちる可能性もあるが)。

readBigTextForEachLine = (file, callback, chunk_size = 1024) => {
    let offset = 0;
    let text = "";
    let slice = "";
    let fr = new FileReader();

    fr.onload = (event) => {
        if(typeof fr.result === "string") {
            callback(text + fr.result.replace(/\r/g, "\n"));
            return true;
        }

        let view = new Uint8Array(fr.result);

        for(let i=0, l=view.length; i<l; i++) {
            if(view[i] === 13) { // \n = 10 and \r = 13
                if(view[i+1] === 10) {
                    i++;
                }

                callback(text + "\n");
                text = "";
                continue;
            }

            text += String.fromCharCode(view[i]);
        }

        seek();
    };

    fr.onerror = () => {
        callback("Failed to read file.");
    };

    let seek = () => {
        if (offset + chunk_size >= file.size) {
            slice = file.slice(offset);
            fr.readAsText(slice);
        } else {
            slice = file.slice(offset, offset + chunk_size);
            fr.readAsArrayBuffer(slice);
        }

        offset += chunk_size;
    }
    seek();
}

使い方の例は以下。第二引数にconsole.logを指定しているので、改行を見つけるたびにコンソールに内容が出力される。

<script>
    document.getElementById("upload").onchange = (e) => {
        let file = e.target.files[0];
        readBigTextForEachLine(file, console.log);
    };
</script>

<html><body>
    Upload Big File: <input type="file" id="upload">
</body></html>