<input type="file">でユーザーが選んだ画像をブラウザ上で即座にプレビュー表示させたい場合、シンプルに実装できる方法の一つがURL.createObjectURL()です。
手軽に使える便利なメソッドですが、実務で扱う際には「不要になったらURL.revokeObjectURL()で不要なObject URLを解放する」というライフサイクルの理解が欠かせません。
この記事では、URL.createObjectURL()を使った画像プレビューの実装から、revokeObjectURL()が必要な理由、差し替え時の解放パターン、FileReaderとの違いやトラブルシューティングまでを解説します。
URL.createObjectURLとは?
URL.createObjectURL()は、ブラウザ内部に存在するFileやBlobなどのデータに対して、アクセスするための一時的なURL(Object URL)を発行するメソッドです。
const file = fileInput.files[0];
const objectURL = URL.createObjectURL(file);
// 例: blob:https://example.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
preview.src = objectURL;
ローカルの絶対パスを取得しているわけではない
初心者がつまずきやすいポイントとして、「PC上のC:\Users\...のような絶対パスを取得している」と誤解してしまうケースがあります。
セキュリティ上の制約から、ブラウザのJavaScriptがユーザーのローカルファイルパスを直接取得することはできません。URL.createObjectURL()が生成するのは、あくまで「ブラウザ内でデータを参照するための一時的なURL(blob:から始まるURL)」です。
input type=”file”で選んだ画像をプレビューする
まずは、画像を選択してプレビューする最小構成を確認します。
<input type="file" id="file" accept="image/*">
<img id="preview" alt="選択した画像のプレビュー">
選択できるファイル形式をaccept属性で指定する場合の注意点や、対象外ファイルを完全には禁止できない理由についてはこちらの記事で解説しています。
const fileInput = document.querySelector('#file');
const preview = document.querySelector('#preview');
fileInput.addEventListener('change', () => {
const file = fileInput.files[0];
if (!file) return;
const objectURL = URL.createObjectURL(file);
preview.src = objectURL;
});
File(選択されたファイルデータ)
↓ URL.createObjectURL(file)
Object URL(blob:https://... のような一時URL)
↓
img.src に代入して画面上に描画
MDNのドキュメントでも、File / BlobをURL.createObjectURL()に渡し、生成したURLを画像・動画・PDFなどの表示に利用するパターンが標準的な手法として案内されています。
revokeObjectURLを呼ばないとどうなる?
URL.createObjectURL()を使う上で知っておくべきなのがURL.revokeObjectURL()による解放処理です。
URL.revokeObjectURL(objectURL);
呼び出すたびに新しいURLが生成される
Object URLが有効な間は、そのURLが参照しているBlob / Fileをブラウザが解放できない状態になります。そのため、不要なObject URLを作り続けると、メモリ使用量が増える原因になります。
特に注意すべきなのは、同じfileに対してURL.createObjectURL()を複数回呼んだ場合でも、既存のURLは再利用されず、毎回新しいURLが発行されるという点です。
const url1 = URL.createObjectURL(file);
const url2 = URL.createObjectURL(file);
const url3 = URL.createObjectURL(file);
// → 同じファイルであっても、3つの独立した Object URL が生成される
ページを閉じたりリロードしたりすればブラウザによって自動的に解放されますが、SPA(Single Page Application)や、ツール系アプリでユーザーが何度も画像をプレビュー・再選択する画面では、不要になった古いURLを明示的に解放しておくのが適切です。
ファイルを選び直す場合は前のObject URLを解放する
実務で画像プレビューを実装する際は、「新しくファイルを選んだタイミングで、直前に作成したObject URLを解放する」パターンが扱いやすく安全です。
const fileInput = document.querySelector('#file');
const preview = document.querySelector('#preview');
// 現在使用中の Object URL を保持する変数
let currentObjectURL = null;
fileInput.addEventListener('change', () => {
const file = fileInput.files[0];
if (!file) return;
// 1. 直前の Object URL が存在していれば解放する
if (currentObjectURL) {
URL.revokeObjectURL(currentObjectURL);
}
// 2. 新しい Object URL を生成して適用する
currentObjectURL = URL.createObjectURL(file);
preview.src = currentObjectURL;
});
【1回目の選択: image-a.jpg】
→ currentObjectURL = blob:xxxxx-A を生成
→ preview.src に設定
【2回目の選択: image-b.jpg】
→ 直前の blob:xxxxx-A を revokeObjectURL() で解放
→ currentObjectURL = blob:xxxxx-B を新しく生成
→ preview.src を更新
この実装にしておくことで、何回画像を選び直しても有効なObject URLは常に最新の1つだけに抑えられます。
画像のload直後にrevokeObjectURLしてもいい?
「画像の読み込みが終わった瞬間に解放すれば無駄がないのでは?」と考え、以下のように書きたくなることがあります。
// 早すぎる revoke の例
preview.addEventListener('load', () => {
URL.revokeObjectURL(objectURL);
});
一見良さそうに見えますが、表示直後のloadイベントで即座に解放するのは早すぎるケースがあるため注意が必要です。
MDNでも注意されている通り、画像を右クリックして「名前を付けて画像を保存」したり、「新しいタブで画像を開く」といったユーザー操作を行おうとした際、すでにURLとリソースの対応付けが解除されているため、保存や別タブ表示に失敗することがあります。
基本的には「画像が読み込まれた瞬間」ではなく、「別の画像に差し替えられた」「プレビュー要素が画面から破棄された」など、そのリソースへアクセスする必要が確実になくなったタイミングで解放するのが適切です。
revokeObjectURLした後はどうなる?
URL.revokeObjectURL()を実行すると、そのObject URLとブラウザ内のBlobデータとの紐付けが解除されます。
const objectURL = URL.createObjectURL(file);
// 解放する
URL.revokeObjectURL(objectURL);
// 解放された後に参照しようとすると失敗する(画像が表示されない)
preview.src = objectURL;
すでに開始済みのリクエストや描画が完了している場合はそのまま表示が残ることもありますが、後からそのURLを使って新しく読み込もうとすると利用できなくなります。そのため、単にsrcへの代入が終わった時点ではなく、そのObject URLを今後ユーザーやコードが参照する必要がなくなった段階で解放します。
createObjectURLとFileReader.readAsDataURLはどう違う?
画像をフロントエンドでプレビューする手法として、FileReaderを使ってData URL(data:image/png;base64,...)へ読み込む方法もあります。
| 比較項目 | URL.createObjectURL() |
FileReader.readAsDataURL() |
|---|---|---|
| 生成される形式 | blob: URL |
data: URL |
| データの扱い | Blob / Fileへの参照URLを作る | ファイル内容をData URLとして読み込む |
| 処理方式 | URL文字列を同期的に取得 | 読み込み完了を非同期で待つ |
| 向いている用途 | 画像・動画などの一時プレビュー | Data URLそのものが必要な処理 |
今回のような「選択した画像をその場でプレビュー表示するだけ」の用途であれば、同期的に短く記述できるURL.createObjectURL()が扱いやすく適しています。
createObjectURLがうまくいかないときの確認点
画像が表示されない、あるいは挙動がおかしい場合は以下のポイントを確認してください。
1. fileオブジェクトが取得できているか
fileInput.files[0]が正しく取得できているかconsole.log(file)で確認します。
2. ファイル未選択時(キャンセル時など)を考慮しているか
ファイルが選択されていない場合は、fileInput.files[0]が取得できません。また、ファイル選択画面をキャンセルした場合の扱いはブラウザや直前の選択状態によって異なるため、if (!file) return;のようなガード節を入れておくと安全です。
3. revokeするタイミングが早すぎないか
img.srcに代入する前にURL.revokeObjectURL()を呼んでしまっていないか、コードの実行順序を確認してください。
4. ファイルを選び直すたびにObject URLを作りっぱなしにしていないか
currentObjectURLなどの変数で管理せず、changeイベント内で解放処理を入れずにcreateObjectURLを呼び続けていないか確認します。
5. srcへFileオブジェクトを直接入れていないか
<img src="...">のsrc属性は文字列(URL)を要求します。Fileオブジェクトそのものを代入してもブラウザは読み込めません。
// ✕ NG:File オブジェクトを直接代入している
preview.src = file;
// ◯ OK:Object URL に変換してから代入する
preview.src = URL.createObjectURL(file);
同じファイルを選び直したときにchangeが発火しない場合は、file inputの値リセットについて解説したこちらの記事も参考にしてください。
Object URLをいつ解放すればいい?
実務におけるrevokeObjectURL()の解放タイミングの考え方を整理します。
| 状況・ユースケース | revoke の判断・タイミング |
|---|---|
| プレビュー画像を別の画像へ差し替える | 直前まで使用していた古いObject URLを解放する |
| プレビュー表示の要素を破棄・削除する | 画面やコンポーネントの破棄時に解放する |
| 一時的なファイルダウンロード処理 | <a>タグのクリック等でダウンロードが開始された後、不要になったら解放する |
| ユーザーが画像を別タブ表示・保存する可能性がある | 即時解放せず、画面破棄や差し替え時まで保持する |
| 通常のページ離脱・画面遷移 | ブラウザがドキュメント破棄時に自動解放する |
まとめ
URL.createObjectURL()は、ブラウザ上でFileやBlobを手軽にプレビュー表示できる便利なAPIです。
- 基本の役割:ローカルの絶対パスではなく、ブラウザ内部のデータを参照する一時的な
blob:URLを発行する - 管理の基本:同じファイルでも呼び出すたびに新規URLが作られるため、不要になったら
URL.revokeObjectURL()で解放する - 実務での定石:ファイル再選択時に「古いURLをrevokeしてから新しいURLを生成する」パターンを採用する
- 解放のタイミング:画像読み込み直後(
load時)の早すぎる解放は避け、要素の破棄や差し替え時を基準にする
「不要になったObject URLは適切に解放する」というライフサイクルを意識して実装しましょう。