アップロードされた画像をlocalStorageに格納する方法
🌱 はじめに
ユーザーによりアップロードされた画像をサーバーやDBなどに格納せず、localStorageに格納したくなったので調査した結果を記載。
🪇 前提
以下で書いてます
📣 言語 → TypeScript
🚋 フレームワーク → Nuxt3
🧑💻 基本のコード
<template>
<input type="file" accept="image/jpg,image/jpeg,image/png,image/gif" @change="uploadImage">
</template>
<script setup lang="ts">
const uploadImage = () => {
console.log('ここにlocalStorageに格納する処理を記載');
};
</script>

上記のコードを実行してみた様子
ここからはこのscriptタグの中身を書き換えていく。
📬 まずはファイルの取得
const uploadImage = (event: Event) => {
const target = event.target;
if (!(target instanceof HTMLInputElement)) return;
const selectedFile = target.files?.[0];
if (!selectedFile) return;
console.log('🐹選択した画像🐹:', selectedFile);
};

これで一旦アップロードした画像がコンソールに表示されるようになった
📥 localStorageに格納しようとしてみる
const uploadImage = (event: Event) => {
const target = event.target;
if (!(target instanceof HTMLInputElement)) return;
const selectedFile = target.files?.[0];
if (!selectedFile) return;
console.log('🐹選択した画像🐹:', selectedFile);
// 追加
window.localStorage.setItem('uploadedImage', selectedFile);
};

実行結果
でもこれでは [object File] という文字列が入っているだけで画像は入ってない
これはlocalStorageのvalueがStringのみ許容しているから。
keyValue
作成または更新したいキーに対して渡したい値を持つ文字列です。
なので以下2つの方法で実施
- 画像自体を文字列化してlocalStorageに格納
- 画像にアクセスできるURLを発行してそのURLを格納
1️⃣ 画像自体を文字列化してlocalStorageに格納
data: URL としてファイルの情報自体を文字列にしてからlocalStorageに格納する。
const uploadImage = async (event: Event) => {
const target = event.target;
if (!(target instanceof HTMLInputElement)) return;
const selectedFile = target.files?.[0];
if (!selectedFile) return;
const imageString = await new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(String(reader.result));
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(selectedFile);
});
window.localStorage.setItem('uploadedImage', imageString);
};
data:image形式で格納できた!

ちなみに
const imageString = await new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(String(reader.result));
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(selectedFile);
});
について解説。
const reader = new FileReader();
画像読み込みツールであるFileReader() を用意しておく。
reader.onload = () => resolve(String(reader.result));
用意ができたら reader.result を文字列にしてreturnする
※ resolveは成功時のreturnって感じ
reader.onerror = () => reject(reader.error);
用意ができなかったら reader.error をreturnする
※ rejectは失敗時のreturnって感じ
reader.readAsDataURL(selectedFile);
ここでアップロードされたファイルの読み込みを開始する
ここに記載されているようにresultに欲しい文字列が入るので、 resolve() にはreader.resultを格納する。
result 属性には、ファイルのデータを表す、base64 エンコーディングされた data: URL の文字列が格納されます。
2️⃣ 画像にアクセスできるURLを発行してそのURLを格納
URL.createObjectURL()を使用して画像に対する一時的なオブジェクトURLを発行しlocalStorageに格納する
const uploadImage = (event: Event) => {
const target = event.target;
if (!(target instanceof HTMLInputElement)) return;
const selectedFile = target.files?.[0];
if (!selectedFile) return;
const objectUrl = URL.createObjectURL(selectedFile);
window.localStorage.setItem('uploadedImageObjectUrl', objectUrl);
};

🌮 2つの方法を比較
| 画像自体の文字列化 | URLを発行 | |
|---|---|---|
| 特徴 | 画像データを文字列化 | 一時的にアクセスできるURLを発行 |
| メリット | 別タブからアクセス可能 | localStorageの容量を逼迫せず格納できる |
| デメリット | localStorageの容量に依存 | 別タブからアクセスできない 同じくリロードでも破棄される |
🎚️ 補足: localStorageの容量
localStorageの容量は 5MiBまでと記載されているため、画像自体の文字列化の場合は5MiBを超過する画像はエラーになってしまう。
ちなみに、5MiBは5.24288MBくらいとのこと。
Uncaught (in promise) QuotaExceededError: Failed to execute 'setItem' on 'Storage': Setting the value of 'uploadedImage' exceeded the quota.

🎡 補足: Base64への変換によるデータ量の増加
data: URLを用いた方法だとデータ量が4/3倍になる。参考
文字列やファイルを Base64 にすると、通常、その文字列のソースよりもおよそ 3 分の 1 大きくなります
したがってdata: URLで格納する場合は 5MiB * 3/4 = 3.75MiB ほどの画像しか格納できないということになる。
📖 まとめ
案として2つ書いたがどちらにも良し悪しがあり、 容量の大きい画像を格納し、別タブで参照したい 場合とかは役割を果たせなさそう...
調べてみたら IndexedDB とか Cache とかのやり方もありそうなので調査。
追記
IndexedDBについて記事にしました↓
Cacheについて記事にしました↓
Discussion