見出し画像

JavaScript 巨大なローカルファイルの部分的な読み込み

<input type="file">で選択したローカルファイルの部分アクセス方法
※OS、ブラウザによっては使えない可能性があります

Fileインターフェイスのslice

<input>で選択した時点では、ファイルは読み込まれません。
FileはBlobの機能を備えています。slice関数を使用すれば、ローカルファイルへの部分アクセスが可能です。

 // File ローカルファイルの部分アクセス
 let blob = file.slice(0, 1024*1024);
 let abuff = await blob.arrayBuffer();

動作確認

メモリ使用量の確認
Windows11 : Chrome / Edge
Windows10 : Edge / FireFox
※OS、ブラウザによって動作が異なる可能性あり
iPad iPhoneなどiOS系は選択した時点ですべて読み込まれているよう挙動でした(推測です 大きな動画ファイルを読み込むと失敗)。

メモリスナップショット(Edge)

ファイルすべて読み込み(65MB)
ファイル 部分読み込み(1MB)
ファイル 4MB単位でストリーミング 

確認用ソースコード

export async function main(){
    let ifile = <HTMLInputElement>document.getElementById("file");
    let signal = new SignalWait<File>();
    ifile.oninput = ()=>{
        if(ifile.files && ifile.files[0]){
            signal.signal(ifile.files[0]);
        }
    }
    
    let file = await signal.wait();
    console.log(file);
    //この時点ではファイルは読み込まれない
    // ※ Windows : Edge/FireFox の場合
    // OSやブラウザによって挙動が変わる可能性があります

    // 全て読み込んだ場合 
    let buff = await file.arrayBuffer();
    // メモリ使用量がファイルサイズ分増加

    // 部分的な読み込み
    file = await signal.wait();
    buff = await file.slice(0, 1024*1024).arrayBuffer();
    // 読み込んだ分だけ使用メモリ増加

    // 連続した部分的な読み込み 解析処理とか
    file = await signal.wait();
    let step = 4*1024*1024;
    for(let i=0;i<file.size/2;i+=step){
        buff = await file.slice(i,i+step).arrayBuffer();
        // 使い終わったら削除
        console.log(buff.byteLength)
    }
    // メモリ使用量の累積なし 読み込んだ分だけ使用メモリ増加

    file = await signal.wait();

    // File ローカルファイルの部分アクセス
    let blob = file.slice(0, 1024*1024);
    let abuff = await blob.arrayBuffer();

}

export class SignalWait<T=any>{
    resolve : any[];
    constructor(){
        this.resolve = [];
    }
    
    async wait() : Promise<T>{
        return new Promise((rs)=>{this.resolve.push(rs);});
    }

    signal(val : T){
        if(this.resolve !== undefined){
            for(let rs of this.resolve){
                rs(val);
            }
            this.resolve = [];
        }
    }
}


この記事が役に立ったという方は、サポートお願いします。今後の製作の励みになります。