MENU

JavaScriptDOMファイル操作同期・非同期

【JavaScript】 createObjectURL()した後にrevokeObjectURL()が必要な理由

更新日:2020/07/27

 

 

URL.createObjectURL()を実行した後、URL.revokeObjectURL()を実行する必要があります。
という説明を見たのだけれど、理由がわからなかったので調べてみました。

 

 

 

※この記事はhttps://www.w3.org/TR/FileAPI/から、createObjectURL()とrevokeObjectURL()に関する部分のみを読んで作成しています。
つまり僕の理解が浅いです。
この記事は厳密な仕様に関するものではなく、考え方として受け取ってください。

オブジェクトURLとは

 

オブジェクトURLとは、オブジェクトに便宜的なユニークなIDを付け、そのIDとURLの種類を組み合わせた文字列です。

 

この文字列はブラウザがURLとして解釈でき、ブラウザがオブジェクトURLを管理する仕組みに渡すことで、対応するデータを取得します。

 

オブジェクトURL

 

もう少し詳しく解説します。

オブジェクトURLの作成

File APIなどで取得したFileオブジェクトを、次のようにURL.createObjectURL()の引数に渡すと、オブジェクトURLを取得できます。

 

オブジェクトURLの取得

 


const objectUrl = URL.createObjectURL( obj );

※ObjはBlobオブジェクトを指定します。FileオブジェクトはBlobから派生しているので指定可能です。

 

取得したオブジェクトURLは、次のような"blob:"で始まる文字列です。

 

"blob:null/f79b06de-2072-4fe4-bb2b-a89383231a79"

 

後ろに続く文字列は、createObjectURL()が呼び出された時に他と重複しないように作成されます。
このときオブジェクトの同一確認をしていないので、同じオブジェクトでも呼び出すたびに異なるオブジェクトURLが作成されます。

 

オブジェクトURLの仕組み

 

Blobから作成したオブジェクトURLは、Blob URLストアにBlobオブジェクトへの参照とペアで格納されています。

 

Blob URLストア:{
    Blob URL1:{ オブジェクトURL , Blobオブジェクトへの参照 }
    Blob URL2:{ オブジェクトURL , Blobオブジェクトへの参照 }
   ・・・・
}

 

URLの参照行為、例えばimgタグのsrcにオブジェクトURLがセットされると、Blob URLストアから一致するものを検索してBlobオブジェクトから、データを取得します。

 

オブジェクトURLはBlobを保持する

 

JavaScriptは他から参照されていないオブジェクトを、ガーベッジコレクションのアルゴリズムに基づいて自動でメモリ上から削除します。
逆に言うと、他から参照されている間はメモリ上に残ります。

 

File APIなどで作成されたBlobオブジェクトは、必要なくなれば削除されます。
しかしURL.createObjectURL()でオブジェクトURLを作成すると、Blob URLストア内にBlobオブジェクトへの参照が作成されるため、必要なくなっても削除対象になりません。

 

Blobへの参照を削除する

 

Blobオブジェクトを削除対象にするには、URL.revokeObjectURL()を使用して、Blob URLストア内から参照を削除する必要があります。

 

オブジェクトURLの破棄

 


URL.revokeObjectURL( objectUrl );

 

revokeObjectURL()は、引数で与えられた文字列(オブジェクトURL)と一致するものを検索して、Blob URLストア内から削除します。

revokeObjectURL()を実行するタイミング

 

revokeObjectURL()は、URLを取得したオブジェクトが不必要になった時点で実行します。

 

例えばimgタグのソース読み込みでは、画像の読み込みが終了または失敗した時点で実行します。

 

createObjectURL()での画像読み込み

 


const img = document.createElement("img");
const imageURL = URL.createObjectURL( fileObj );

img.onload = ()=>{
                   URL.revokeObjectURL(imageURL);
                   // imgをDOMに追加するなどの処理
               };
img.onerror = e=>{
                   URL.revokeObjectURL(imageURL);
               };
img.src = imageURL;

 

ただし同じURLを共有する場合は、注意が必要です。

 

createObjectURL()での複数画像を読み込み:失敗パターン

 


const img1 = document.createElement("img");
const img2 = document.createElement("img");

const imageURL = URL.createObjectURL( fileObj );

img1.onload = img2.onload = e=>{
                   URL.revokeObjectURL( imageURL );
               };
img1.onerror = img2.onerror = e=>{
                   URL.revokeObjectURL( imageURL );
               };
img1.src = img2.src = imageURL;

 

上のコードは一方のimgタグしか画像が表示されません。

 

どのような順番でイベントが発生するかわかりませんが、例えば最初にimg1で読み込み完了イベントが発生したすると、その時点でオブジェクトURLが失われてしまうので、img2はデータを読み込むことができないからです。

 

上手く読み込ませるには、次の例のように URL.createObjectURL()を2回呼び出すなどの工夫が必要です。

 

createObjectURL()での複数画像を読み込み:改良パターン

 


const img1 = document.createElement("img");
const img2 = document.createElement("img");

img1.onload = img2.onload = e=>{
                   URL.revokeObjectURL( e.target.src );
               };
img1.onerror = img2.onerror = e=>{
                   URL.revokeObjectURL( e.target.src );
               };
img1.src = URL.createObjectURL( fileObj );
img2.src = URL.createObjectURL( fileObj );

記事の内容について

 

こんにちはけーちゃんです。
説明するのって難しいですね。


「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。

そんなときは、ご意見もらえたら嬉しいです。

ご意見はこちら。
https://affi-sapo-sv.com/info.php