画像ファイルのアップロード②(HTML+JavaScript(jQuery)+PHP)
HTML5の機能以上を表現したければ、JavaScriptのプログラムが利用可能です。ただし、JavaScriptだけをゴリゴリに書くとブラウザ互換の問題があるやもしれません。そのため、この記事では少しでも互換性をカバーしてくれるJavaScriptのライブラリであるjQueryを利用して動きをつけています。jQueryはバージョンアップは、安定しているであろう 3.4.1 を利用しました。
ファイルアップロード前に画像を表示する画面と流れ
プログラムを作るとき、特にWebアプリであれば、どういうインターフェースにするのかを画面で表現したり、操作によりどちらの画面へ流れて行くかなどを考えたり、画面遷移(こりゃまた難しい言葉ですね)の概要を示せれば、初心者でもプログラムを作り始めた後に迷うことが少ないでしょう。
一番最初のアップロードファイルを選ぶ画面は見た目だけだと前のバージョンとまったく同じで、次の様になります。
ファイルを選ぶダイアログの操作もWebブラウザというよりも、Webブラウザが動いているWindowsやMacOSなどのOSが提供している機能そのものであるため、操作・画面が共に1回目とまったく同じです。
そして、見栄えが違ってくるのが次の画面で、ファイルをドラッグ&ドロップ または ファイル選択 した状態は次の様にする予定です。
前回と何が違うかというと、[ファイルを選択] ボタンの下に、選んだファイルの画像 を表示しました。話を[ファイルを選択] ボタンに戻すと、現段階ではオリジナルのファイル選択をするボタンを使うため、ドラッグ&ドロップ に対応しているか否かが、初心者には不明確かもしれません…。
最終的なファイルを受け取るページの画面は初回とまったく同じになるだけでなく、プログラムも同じとなり、動作確認ができるサンプル・ページのアドレスは次となります。
https://openpne.sakura.ne.jp/note/file2.html
<input type="file">タグからPHPへファイルを送信
最初のページの画面はオリジナルと同一であったのに対し、HTMLファイル( file2.html )は画像を表示するためにオリジナルから3行だけ新しいタグが追加されています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ファイル送信ページ2</title>
</head>
<body>
<form method="post" enctype="multipart/form-data" action="file2.php">
<input type="file" name="up">
<input type="submit" id="sm" value="アップロード">
</form>
<p id="pics"></p>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="file2.js"></script>
</html>
HTMLファイル( file2.html )で追加されたラグは、12行目( <p id="pics"></p> )が手始めに見つけられるでしょう。<p>タグには後で画像を追加する手がかりとなるための id 属性が必要となり、"pics" を指定しました。
次の追加は14行目(<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>)で、<script>タグでjQueryライブラリの読み込みです。jQueryは配布ファイルを直に置くのではなく、ネットワークにあるCDN(Content Delivery Network)のURLを指定しました。CDNはサービスを提供しているGoogleなどのサイトがあり、多くのWebアプリが共同で使えば使う程にファイルをキャッシュし、ネットワークから毎回毎回読み込みをしなくなれば、全体の動作がほんの少しだけ早くなるであろうという算段です。
最後の15行目( <script src="file2.js"></script> )は、<script>タグでオリジナル記述のJavaScriptのプログラム( file2.js )を読み込んでいます。少し前までは、JavaScriptの読み込みを<head>タグ〜</head>タグの間に書いていたのに対し、スクリプトを読み込んでいる間に<body>タグがなおざりになるから…</body>タグの後がいいだろうという都市伝説の様な話があるため、最近では最後の方に書くようになりました(笑)。
JavaScriptでドラッグまたは選択したファイルを表示
今回からJavaScriptのプログラムを書いて、動きを付けていきます。JavaScriptを地道に書くだけではなく、便利な機能を提供してくれるjQueryという結構ありがたいライブラリを使って手抜きをしました。ファイル表示に対応したJavaScriptファイル( file2.js )は、次となります。
$(function() {
$('input[name="up"]').on("change",function(e){
let fs = this.files;
$("#pics").html("");
if(0 < fs.length){
let fR = new FileReader();
fR.onload = function(e){
let src = e.target.result;
$('<img>', { 'class':'picture', 'alt':fs[0].name, 'src': src }
).appendTo('#pics');
}
fR.readAsDataURL(fs[0]);
}
});
})
1行目( $(function() { )〜15行目( }) )は「$(document).ready(function(){〜})」という記述の省略形で、『HTMLファイルが最後まで読み込まれて解釈されたら「〜」の部分を実行する』という意味です。jQueryの書きはじめとしてよく使われます。
2行目( $('input[name="up"]').on("change",function(e){ )〜14行目( }); )は<input>タグでname属性が "up" のオブジェクトに "change" イベントが発生したら(function(e){〜}); )を実行するの意味です。すなわちファイルの部品にファイルがドロップされるか、またはファイルの部品をクリックしてファイルが選ばれたら、次の3〜12行目までを実行。あと、"drop" というイベントもあるとはいえ、"drop"イベントを受けると、その後に "change" イベントも発生するので、今回は "change"イベントのみに対応しました。
3行目は受け取ったファイルデータなどの配列をfs配列に代入し、4行目は画像を表示する "#pics" が指し示す<p id="pics">タグと</p>タグの間をhtml()関数でクリアしました。5行目はfs配列のlength(受け取ったファイル数)が0より多ければ以降(6〜12行目)の処理を続けます。
6行目でFileReaderオブジェクトを作り、fR変数に保存しました。FileReaderオブジェクトはクライアントのファイルへアクセスするためにHTML5から用意された File API のことです。
7行目( fR.onload = function(e){ )〜11行目( } )はファイルが読み込まれ(onloadし)た時に呼び出される関数(8〜10行目)を定義し、関数にはファイル情報がe変数として渡されます。8行目は受け取ったファイルのデータ( 「data:image/png;base64,」などで始まるエンコードされている画像データ )を src変数に代入。ちなみに、画像データはバイナリデータ(テキストエディタでは開けない)で、後々にバイナリデータへ戻せる(デコードできる)テキストに変換されています。9,10行目ではclass属性が 'picture' 、alt属性が受け取りファイル名( fs[0]はfs配列の1つ目のファイル情報で、nameはそのァイル名)、src属性がsrc変数、となる<img>タグを作り、 "#pics" が指し示す先程の<p id="pics">タグと</p>タグの間に追加しました。
12行目は関数定義が終わった後で、readAsDataURL()関数を使ってfs[0](fs配列の1つ目のファイル)を読み込み、ファイルの読み込みが終わると、7〜11行目のonload後の関数が呼び出されます。ファイル読み込み後の処理は、終わるのを待ってから次の処理をする順次駆動ではなく、終わると他の処理へ進むけれど呼び出されるので、非同期と呼ばれるプログラムです。
今回のページでは画像を読み込めても、読み込めなくても、[アップロード] ボタンをクリックすると次の file2.php へアクセスします。
<?php
$up_file = "";
$up_ok = false;
$tmp_file = isset($_FILES["up"]["tmp_name"]) ? $_FILES["up"]["tmp_name"] : "";
$org_file = isset($_FILES["up"]["name"]) ? $_FILES["up"]["name"] : "";
if( $tmp_file != "" &&
is_uploaded_file($tmp_file) )
{
$split = explode('.', $org_file); $ext = end($split);
if( $ext != "" &&
$ext != $org_file )
{
$up_file = "img/". date("Ymd_His.") . mt_rand(1000,9999) . ".$ext";
$up_ok = move_uploaded_file( $tmp_file, $up_file);
}
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ファイル受信ページ1</title>
</head>
<body>
<p><?php if( $up_ok ){ ?>
アップロードされたファイルは <img src="<?= $up_file ?>"> です。
<?php }else{ ?>
アップロードは失敗しました。
<?php } ?></p>
<a href="file2.html">アップロードページへ戻る</a>
</body>
</html>
最後に、一応、file2.php のファイルを掲載しておくと、前回お見せしたプログラムと中身はまったく同じです。実際に運用するプログラムでは、ファイルの種類をチェックしたり、ファイルのサイズをチェックして対処するなどの細かな処理を追加する必要があるでしょう。
まとめ
今回は、jQueryを利用したJavaScriptのプログラムが追加され、少しだけ一般に出回っているプログラムに近づけた気がします。ただ、ファイルを選択するボタンの操作はわかる人にはわかるけれど、『小さなお子さんからお年寄りまでを相手にした丁寧な作りである』とは言えそうにありません。次回はもう少し突っ込んで、誰にでもわかりやすいインターフェースに挑戦してみようと思います。