見出し画像

JavaScript fetch関数で巨大ファイルの部分的な読み込み

fetch( URL )だと全ファイルを読み込んでしまうため、追加のオプション設定でファイル情報を取得、部分的な読み込みを行う方法です。基本的な情報は無料、実践的な情報とソースコードは有料です。
XMLHttpRequestなどの必要な知識があれば簡単に実装できるのですが、知らなかった私は、情報が見つからず結局実装するのに1年ほどかかりました。探し方が悪いのか必要とされていないのか。
※開発環境 Windows10/11 Edge/Chrome
※OS/ブラウザによって動作が異なる可能性あり

実装する機能

  • ファイル情報の取得

  • ファイルの部分読み込み

ファイル情報の取得

fetchのoption設定に {method:"HEAD"}
レスポンスのheadersで様々なファイル情報が取得可能です。

    let resp = await fetch(url,{method:"HEAD"});
    
    // ファイルサイズ
    let length = resp.headers.get("Content-Length");
   
    // 部分的なアクセスに対応?
    let range = resp.headers.get("Accept-Ranges");
    // range == "bytes"

    // 日時 ファイルタイプ
    let date = resp.headers.get("Last-Modified");
    let ctype = resp.headers.get("Content-Type");

ファイルの部分読み込み

fetchのoption.headersとHeadersクラスを使用します

    // 100~200の100byte読み込み
    let begin = 100;
    let end = 200;
    let headers = new Headers();
    headers.append( "range", "bytes=" + begin + "-" + (end-1) );
    let read = await fetch(url,{headers});
    let buff = await read.arrayBuffer();

動作確認

Windows10/11 Edgeの開発者ツール(ネットワーク)で確認
fetch(url)での全ファイル読み込み
ファイル情報取得での通信データサイズ
部分読み込みでの通信データサイズ

通信データサイズ確認

有料 ファイル読み込みクラス

記事が役に立った!すぐに使えるソースコードがほしい!
という方は、記事の購入をお願いします。

fetch関数を利用したファイル読み込みクラスのソースコードです。
ファイルオープン、ファイル情報取得、部分的な読み込み、基本的なエラー処理(単純なthrow処理)

class FetchFile{
    size : number; // ファイルサイズ
    dateModify : Date; // 更新日時
    type : string; // Content Type

    // ファイルオープン
    // ファイル情報の取得可能に サイズなど
    async open(url : string);

    // ファイルの部分読み込み begin ~ end-1
    // ret : fetch Response
    async read(begin : number, end : number);

    async arrayBuffer(begin : number, end : number){
        let r = await this.read(begin, end);
        return r.arrayBuffer();
    }

    async blob(begin : number, end : number){
        let r = await this.read(begin, end);
        return r.blob();
    }

    // error -> throw "error text"
}
class FetchFile{
    url : string;
    size : number;
    status : string;
    dateModify : Date;
    type : string;

    constructor(){
        this.url = "";
        this.size = 0;
        this.status = "null";
        this.type = "";
        this.dateModify = new Date();
    }

    // ファイルオープン
    // ファイル情報の取得 サイズなど
    async open(url : string){
        // ファイル情報取得
        let resp = await fetch(url,{method:"HEAD"});
        this.url = "";
        this.size = 0;
        this.type = "";
        if(!resp.ok){
          throw "FetchFile Response " + resp.status;
        }
        if(resp.status != 200){
            throw "FetchFile " + resp.status;
        }
        let length = resp.headers.get("Content-Length");
        let range = resp.headers.get("Accept-Ranges");
        let date = resp.headers.get("Last-Modified");
        let ctype = resp.headers.get("Content-Type");
        if(length === null || range ===null || date==null || ctype==null){
            throw "FetchFile Header " + resp.status;
        }
        if(range != "bytes"){
            throw "FetchFile Accept-Ranges"  + resp.status;;
        }
        let size = parseInt(length);
        if(isNaN(size)){
            throw "FetchFile Content-Length"  + resp.status;;
        }
        this.type = ctype;
        this.url = url;
        this.size = size;
        this.status = "open";
        this.dateModify = new Date(date);

    }

    // ファイルの部分読み込み begin ~ end-1
    // ret : fetch Response
    async read(begin : number, end : number){
        if(this.status != "open"){
            throw "FetchFile open";
        }
        let headers = new Headers();
        if(begin < 0)begin = 0;
        if(end < 1){
            throw "FetchFile range " + begin + " " + end;
        }
        headers.append("range","bytes="+begin + "-" + (end-1));    
        let r = await fetch(this.url,{headers});
        if(!r.ok){
            throw "FetchFile Response";
        }
        if(r.status != 206){
            throw "FetchFile " + r.status;
        }
        return r;
    }

    async arrayBuffer(begin : number, end : number){
        let r = await this.read(begin, end);
        return r.arrayBuffer();
    }
    async blob(begin : number, end : number){
        let r = await this.read(begin, end);
        return r.blob();
    }
}

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