[ガイド] ブラウザで Base64 エンコードされた画像をプレビューする方法
Base64 エンコードされた画像をページに表示するための実証済みの 5 つの方法 — src への直接代入、FileReader、canvas、Blob 変換、そしてフレームワーク固有のパターン。デバッグのコツも収録。
// 30 秒で分かる答え
Base64 エンコードされた画像文字列を、とにかくページ上で見たいだけなら、data: URI として img.src に代入するのが最短ルートです。JavaScript はたった 3 行です。
const img = new Image();
img.src = 'data:image/png;base64,' + base64String;
document.body.appendChild(img);
// If the string already starts with 'data:image/...;base64,'
// — just assign it directly without prepending anything:
img.src = base64StringWithDataUriPrefix;
// 方法 1: img.src — 直接的なアプローチ
すべての <img> 要素はデータ URI を通常の URL として扱います。URI を src に代入すれば、デコード、フォーマット検出、レンダリングはブラウザが処理してくれます。
これはブラウザがネイティブにサポートする任意の画像フォーマットで動作します。PNG、JPEG、GIF (アニメーション・静止両方)、WebP、SVG、BMP、ICO など。Base64 文字列は、API レスポンス、localStorage、クリップボードからのペースト、データベースの行など、どこから来たものでも構いません。
データ URI 内の MIME タイプは、古いブラウザや一部の組み込み WebView では重要です。生の Base64 ペイロードしかなく (data: プレフィックスなし)、フォーマットが分からない場合は、マジックバイトを調べる (方法 5 を参照) か、image/png をデフォルトにすることができます。たいていのブラウザは MIME タイプが間違っていてもスニッフィングして救ってくれます。
// Plain HTML — no JavaScript needed if the string is static
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..." alt="preview">
// Dynamic JavaScript — load from a variable, fetch, or input field
function renderBase64Image(base64, mimeType = 'image/png', containerId = 'preview') {
const container = document.getElementById(containerId);
const img = new Image();
img.onload = () => {
container.innerHTML = '';
container.appendChild(img);
};
img.onerror = () => {
container.textContent = 'Invalid image data';
};
// Tolerate both raw Base64 and full data: URIs
img.src = base64.startsWith('data:')
? base64
: `data:${mimeType};base64,${base64}`;
}
renderBase64Image(myBase64String, 'image/jpeg');
// 方法 2: ユーザーがアップロードしたファイルに対する FileReader
ユーザーが <input type="file"> 経由で画像をアップロードする場合、自分で Base64 エンコードする必要はありません。FileReader.readAsDataURL() は完全なデータ URI を返してくれるので、そのまま img.src に放り込めます。
<input type="file" id="fileInput" accept="image/*">
<img id="preview" alt="">
<script>
document.getElementById('fileInput').addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
// event.target.result is a complete data URI
// e.g. 'data:image/png;base64,iVBORw0KGgo...'
document.getElementById('preview').src = event.target.result;
};
reader.onerror = () => {
console.error('FileReader failed:', reader.error);
};
reader.readAsDataURL(file);
});
</script>
// 方法 3: 生成・加工された画像のための canvas
<canvas> 上で画像を描画・加工した場合、canvas.toDataURL() は結果をデータ URI として 1 回の呼び出しでエクスポートします。グラフのスクリーンショット、サインパッド、QR コード、ウォーターマークなど、クライアントサイドで生成して画像として表示・ダウンロードしたいものに便利です。
// 1. Draw on canvas
const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 200;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#00FF41';
ctx.fillRect(0, 0, 200, 200);
ctx.fillStyle = '#000';
ctx.font = '20px monospace';
ctx.fillText('Hello, Base64!', 20, 100);
// 2. Export to data URI
const pngDataURL = canvas.toDataURL('image/png');
// pngDataURL === 'data:image/png;base64,iVBORw0KGgo...'
// 3. Display directly
const img = new Image();
img.src = pngDataURL;
document.body.appendChild(img);
// JPEG with quality control (0–1)
const jpegDataURL = canvas.toDataURL('image/jpeg', 0.85);
// WebP if supported
const webpDataURL = canvas.toDataURL('image/webp', 0.9);
// 方法 4: リモートの Base64 ペイロードに対する fetch + Blob
Base64 ペイロードが大きい (50 KB 超) 場合や、何度も表示する場合、URL.createObjectURL() 経由で Blob URL に変換する方が、データ URI のすべてを DOM に保持し続けるよりメモリ効率に優れます。Blob URL は短く (blob:https://example.com/12345-abcde)、不要になれば破棄でき、DOM 文字列における Base64 の 33% オーバーヘッドを回避できます。
// Convert a Base64 string to a Blob, then to a blob: URL
function base64ToBlobURL(base64, mimeType = 'image/png') {
// Strip the data: prefix if present
const payload = base64.includes(',') ? base64.split(',')[1] : base64;
// Decode Base64 to binary string
const binary = atob(payload);
// Convert binary string to Uint8Array
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
// Wrap in a Blob and create an object URL
const blob = new Blob([bytes], { type: mimeType });
return URL.createObjectURL(blob);
}
// Usage
const blobURL = base64ToBlobURL(myBase64, 'image/jpeg');
const img = new Image();
img.src = blobURL;
document.body.appendChild(img);
// IMPORTANT: free the memory when you're done with the image
img.onload = () => URL.revokeObjectURL(blobURL);
// 方法 5: マジックバイトからフォーマットを自動検出する
生の Base64 ペイロード (data: プレフィックスなし) を受け取って、PNG・JPEG・GIF・WebP のいずれか分からない場合は、デコード後の最初の数バイトを覗いてみましょう。すべてのフォーマットには固有のシグネチャがあります。
function detectImageFormat(base64) {
// Look at the first ~12 characters of the Base64 string
const head = base64.replace(/^data:[^,]+,/, '').slice(0, 16);
if (head.startsWith('iVBORw0KGgo')) return 'image/png';
if (head.startsWith('/9j/')) return 'image/jpeg';
if (head.startsWith('R0lGOD')) return 'image/gif';
if (head.startsWith('UklGR')) return 'image/webp';
if (head.startsWith('PHN2Zy') ||
head.startsWith('PD94bWw')) return 'image/svg+xml';
if (head.startsWith('Qk')) return 'image/bmp';
if (head.startsWith('AAABAA')) return 'image/x-icon';
return 'application/octet-stream'; // unknown
}
// Build a correct data URI even when the user only gave us the payload
function toDataURI(base64) {
if (base64.startsWith('data:')) return base64; // already complete
const mime = detectImageFormat(base64);
return `data:${mime};base64,${base64}`;
}
// React — コンポーネントで Base64 画像を表示する
React では、データ URI を他の文字列 prop と同様に扱います。入力がほとんど変わらない場合は変換結果をメモ化しましょう。
import { useMemo } from 'react';
function Base64Image({ base64, mimeType = 'image/png', alt }) {
const src = useMemo(() => {
if (!base64) return '';
return base64.startsWith('data:')
? base64
: `data:${mimeType};base64,${base64}`;
}, [base64, mimeType]);
if (!src) return <div>No image</div>;
return <img src={src} alt={alt} loading="lazy" />;
}
// Usage
<Base64Image
base64={apiResponse.avatar}
mimeType="image/jpeg"
alt="User avatar"
/>
// For large images, prefer blob URLs (Method 4) and useEffect for cleanup:
function LargeBase64Image({ base64 }) {
const [blobURL, setBlobURL] = useState('');
useEffect(() => {
const url = base64ToBlobURL(base64, 'image/png');
setBlobURL(url);
return () => URL.revokeObjectURL(url); // cleanup on unmount
}, [base64]);
return blobURL ? <img src={blobURL} alt="" /> : null;
}
// Vue 3 — Composition API の例
考え方は同じで、Vue の構文に置き換えるだけです。
<script setup>
import { computed } from 'vue';
const props = defineProps({
base64: String,
mimeType: { type: String, default: 'image/png' },
alt: { type: String, default: '' }
});
const src = computed(() => {
if (!props.base64) return '';
return props.base64.startsWith('data:')
? props.base64
: `data:${props.mimeType};base64,${props.base64}`;
});
</script>
<template>
<img v-if="src" :src="src" :alt="alt" loading="lazy" />
<div v-else>No image</div>
</template>
// Svelte — リアクティブ宣言
<script>
export let base64 = '';
export let mimeType = 'image/png';
export let alt = '';
$: src = base64
? (base64.startsWith('data:') ? base64 : `data:${mimeType};base64,${base64}`)
: '';
</script>
{#if src}
<img {src} {alt} loading="lazy" />
{:else}
<div>No image</div>
{/if}
// エラーハンドリング — 何が壊れて、なぜ壊れるのか
画像が壊れたアイコンのプレースホルダとして読み込まれる。 Base64 ペイロードが切り詰められている、文字列の途中に空白が含まれている、あるいは標準のデータ URI 内で URL セーフ文字 (-_) を使っているケースです。str.replace(/\s+/g, '') で空白を取り除き、str.replace(/-/g, '+').replace(/_/g, '/') で URL セーフ版を標準版に戻し、長さが正しいことを確認します (Base64 の長さは 4 の倍数で、適切な = パディングが必要です)。
画像はレンダリングされるが、フォーマットが間違っている。 data:image/png;base64, プレフィックスは PNG だと言っているのに、実際のバイト列は JPEG というケース。ブラウザはマジックバイトをスニッフィングして正しくレンダリングしますが、一部のヘッドレスブラウザや組み込み WebView はそうしません。方法 5 を使ってバイト列から本当のフォーマットを検出しましょう。
画像が DOM 上で巨大になり、ページがもっさりする。 React コンポーネント内の 1 MB の Base64 文字列は、状態が更新されるたびに再レンダリングされます。blob: URL (方法 4) に切り替えれば、DOM 上の文字列は画像サイズに関係なく 50 文字程度になります。
Chrome では動くが、Safari では動かない。 Safari は CSP のデフォルトがより厳しく、一部のコンテキスト (<a download>、Service Worker のフェッチなど) でデータ URI を拒否します。Safari でテストし、必要なら blob: URL にフォールバックしましょう。
Base64 文字列そのものが無効。 受け取った「Base64」が二重エンコードされていたり、16 進エンコードだったり、単に壊れていたりすることもあります。コードをデバッグする前に、まず Base64 → 画像デコーダ に貼り付けて有効性を確認しましょう。
// パフォーマンス: data: URI vs blob: URL — 簡易ベンチマーク
<img> で表示する 500 KB の JPEG の場合:
• src の data: URI: DOM 上で 668 KB の文字列 (Base64 のオーバーヘッド 33%)。ブラウザは URI をパースし、Base64 をデコードし、JPEG をデコードします。中位ノート PC で初回ペイントは約 80 ms。
• URL.createObjectURL 経由の blob: URL: DOM 上で 56 文字の文字列。ブラウザはメモリ上の Blob をフェッチし、JPEG をデコードします。初回ペイントは約 40 ms。メモリ消費は約 50% 削減。
5 KB のアイコンであれば違いは目に見えず、どちらも 5 ms 未満でレンダリングされます。要点: 50 KB 程度までは扱いやすい方を使い、それを超えるなら Blob URL を選びましょう。
// プレビューを単独でテストする
バグを疑ったら、まず Base64 文字列をアプリケーションコードから切り離しましょう。手早い 3 つのチェック方法を紹介します。
1. オンラインツールに貼り付ける。 文字列を Base64 → 画像コンバータ に投入します。そこで画像が正しくレンダリングされるなら、データは健全でバグはあなたのコード側にあります。そこでもレンダリングされないなら、データは上流ですでに壊れています。
2. 新しいタブで URL として開く。 データ URI (data:image/png;base64, プレフィックス込み) をコピーし、新しいブラウザタブに貼り付けて Enter を押します。モダンなブラウザは画像を直接表示します。(アドレスバーの data: をブロックするブラウザもあるので、その場合はステップ 1 のインラインテストを使ってください。)
3. 長さをチェックする。 有効な Base64 文字列の長さは (空白を除いた後) 4 で割り切れます。パディング (=) が差分を埋めます: 末尾の = は 0〜2 文字。長さが 4 で割り切れない場合、その文字列は切り詰められているか余分な文字が付いています。
// Quick validation snippet
function looksLikeValidBase64(str) {
const cleaned = str.replace(/^data:[^,]+,/, '').replace(/\s+/g, '');
if (cleaned.length === 0) return false;
if (cleaned.length % 4 !== 0) return false;
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)) return false;
return true;
}
// 30 秒チートシート
最速: データ URI を img.src に代入。以上。
ファイルアップロードから: FileReader.readAsDataURL(file)。
canvas から: canvas.toDataURL('image/png')。
大きい・繰り返し使う画像には: Blob に変換し、URL.createObjectURL(blob) を使用。
フォーマット不明: データ URI を組み立てる前にマジックバイトをスニッフィング (iVBORw0KGgo = PNG、/9j/ = JPEG など)。
デバッグ: まず Base64 → 画像コンバータ に貼り付け、データ自体に問題があるかを切り分けます。
関連記事: データ URI を理解する · Base64 による画像埋め込み · JavaScript における Base64: ブラウザと Node