ブラウザで画像ファイルを選択した時にプレビュー表示する方法

10年くらい前のような気がしますが、その頃はブラウザで画像アップロードするときのプレビュー機能はWebサイト上でワリと普通に実装されていたと記憶しています。

どういうコーディングだったかはうろ覚えなのですが、たしかinputタグのonchangeで渡されたファイル名をそのままimgタグのsrcに投げてあげれば表示されたような。

しかしそれだとセキュリティに難ありということで、その後のアップデートで使えなくなってしまいました。社内でしか使わない業務系のWebシステムでは互換モードか何かを使ってその場凌ぎをしたような記憶もw

その後は、できないのなら仕方ないということで、いったんテンポラリディレクトリへアップロードをした後にプレビューという名の実際にアップされた画像ファイルを表示し、[OK]なら本ファイルとしてサーバー内でコピー、キャンセルならテンポラリを削除、みたいなことを長らくやっていました。ただ、それだとテンポラリにゴミが残る可能性があるので、cronでお掃除したり。

転機が訪れたのはJavaScriptでFileAPIが実装された頃です。

IEでいうとver10以降、Chromeだとv7以降くらいの話なので、ここ5~6年のことですね。

imgタグのsrcにはデータをそのまま記述できる

通常、imgタグには test.jpg とかファイル名を指定しますが、base64エンコーディングしたデータそのものを指定することもできます。

■imgタグにデータを埋め込む実例

<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" >

data:image/gif; で、これはGIFデータだよ、と指定し、その後のbase64以降が実データです。

上の例だと1pxの空画像なのですが、「おそらく最も小さいブランク画像だよ」と海外のフォーラムで紹介されていたので、使っている人もけっこういるんじゃないかなと思います。実際に上の文字列を数えてみればわかりますが、わずか74文字(74バイト)ですからね。

まぁ今回大事なのはソコじゃなくて、imgタグのsrcにはbase64データが埋め込めるんだよ、という話です。

IE8の頃は32KBまでという制限があったのだけれど、今では2MBくらいまでいけるはずなので実用的になりました。

更にJavaScriptにFileAPIが実装され、そのFileReaderオブジェクトでファイルを読み込むとbase64エンコーディングされたData URIが取得できます。ということは、それをそのままimgタグのsrcにセットしてやるだけでプレビューができちゃう。

JavaScriptとimgタグでプレビューを実装する例

すんごい簡単なのでHTMLまるごと載せます。

■JavaScriptのFileAPIで画像のプレビュー

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScriptのFileAPIで画像のプレビュー</title>
</head>
<body>
<form>
<input type="file" accept='image/*' onchange="previewImage(this);">
</form>
<p>
Preview:<br>
<img id="preview" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" style="max-width:200px;">
</p>
<script>
function previewImage(obj)
{
	var fileReader = new FileReader();
	fileReader.onload = (function() {
		document.getElementById('preview').src = fileReader.result;
	});
	fileReader.readAsDataURL(obj.files[0]);
}
</script>
</body>
</html>

■実行例

JavaScriptで画像プレビュー

■解説

解説も何も実質3行くらいしかないのですが…

  1. FileReaderをロード
  2. 選択された画像ファイルをreadAsDataURLで読み込み
  3. 読み込み完了したらimgタグのsrcにセット

こんだけです。

HTMLのほうとしてはあらかじめ1pxサイズの画像データを表示しておいて、JavaScriptで入れ替えさせる感じですね。

プレビューだけで良いなら、これが一番らくちんじゃないでしょうか。

実行できるサンプル

この下に先程のJavaScriptとimgタグでプレビューする実例を記述しました。

[ファイルを選択]ボタンで画像ファイルを選択するとプレビューが表示されるハズです。
(ファイルがサーバーに送信されたりはしません)

Preview:

JavaScriptとcanvasタグでプレビューを実装する例

次にHTML5で実装されたcanvasタグに描画する例です。

■JavaScriptのFileAPIとcanvasで画像のプレビュー

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScriptのFileAPIとcanvasで画像のプレビュー</title>
</head>
<body>
<form>
<input type="file" accept='image/*' onchange="previewImage(this);">
</form>
<p>
Preview:<br>
<canvas id="preview" style="max-width:200px;"></canvas>
</p>
<script>
function previewImage(obj)
{
	var fileReader = new FileReader();
	fileReader.onload = (function() {
		var canvas = document.getElementById('preview');
		var ctx = canvas.getContext('2d');
		var image = new Image();
		image.src = fileReader.result;
		image.onload = (function () {
			canvas.width = image.width;
			canvas.height = image.height;
			ctx.drawImage(image, 0, 0);
		});
	});
	fileReader.readAsDataURL(obj.files[0]);
}
</script>
</body>
</html>

実行例は同じなので省略します。

■解説

  1. FileReaderをロード
  2. 選択された画像ファイルをreadAsDataURLで読み込み
  3. 読み込み完了したらメモリ中にimageオブジェクトを作成してデータをセット
  4. 作ったimageオブジェクトをcanvasに描画

メモリ中にimageオブジェクトを作ったり、canvasのサイズ指定などがあるので、わずかばかりコード量が増えていますね。

じゃあなんでわざわざcanvasを使うかというと、画像加工ができるからです。

例えばlineToで線を描いたり、fillTextで文字を埋め込んだり、drawImageで背景透過画像を重ね合わせたり、などなど。

表示上だけのことで良ければCSSでも同じようなことはできますけど、画像加工した結果をファイルとして保存したい場合、CSSじゃ困りますよね。

そんなときcanvasで画像加工して、dataURIを使ってファイルに保存、みたいな。

プレビューというよりはリアルタイム加工ですけど、先述のimgタグのsrcにセットする例だけだと寂しいかなと思って一応紹介してみました。

まとめ

  • JavaScriptのFileReaderで画像を読んでimgのsrcにセットするだけでプレビューは簡単に実装できる
  • 画像加工したいならcanvasも使うといいかもね
いじょ。