ファイル添付機能を、確認画面付きのフォームに実装する方法
はじめに
こんにちは!
ポートでエンジニアをしている、@shoya.iikawaです。
今回は、つい先日にファイル添付機能&確認画面が付いたフォームを実装したので、その方法を書いて行こうかな、と思います。
というのも、ファイル添付単体であれば、<input type="file">
を使いながら比較的簡単に実装出来ますし
確認画面単体であれば、<input type="hidden" />
を使って「入力画面->確認画面->送信画面」と入力値を受け渡せば比較的簡単に実装出来ます。
しかし、確認画面&ファイル添付となると、添付されたファイルそのものは<input type="hidden" />
で受け渡せ無いので、ちょこっと工夫がいるんですよね🧐
という事で、以下がどなたかの参考になれば嬉しいです!😀
前提
データが送られてきた事を確認するisset($_POST)
や、フォームにとって何より大事なバリデーションあたりは全部省いて、極力ifを減らして要点に絞ったコードサンプルとなっているので、ご理解ください🙏
とは言え、ファイルアップロードの方法を求めてこの記事を開いて下さる方もいると思うので、ファイルアップロード関係のバリデーションだけは最後のまとめに簡単に書いておこうと思います。
まずは確認画面付きフォームを用意
手始めに、まずは確認画面が付いたシンプルなフォームを用意します。
index.php
フォームのTOP画面です。
お名前と問い合わせ内容の入力項目を用意しています。
ここで入力された内容は、次のconfirm.php
へと受け渡されます。
confirm.php
確認画面です。
index.php
から送信された情報を、エンドユーザー向けにはテキストで出力しつつ、<input type="hidden" />
を利用して、次の送信画面に受け渡します。
send.php
送信処理&送信完了画面です。
confirm.php
から送信された情報を、PHPMailerというライブラリを使ってメールで送信しつつ、完了画面を表示しています。
添付ファイル機能を実装
さて、ここから本題です!
先述の確認画面付きフォームに、添付ファイル機能を付け足していきます。
要点は以下の通りです。
-
index.php
から送信されたファイルは、PHPによって一時的にサーバー内に保管される - そのファイルを
send.php
で扱う為、confirm.php
の段階で、プログラム側で決めた一時ディレクトリに移動させる - この時、一時ディレクトリに保管するファイル名はユニークなもので無ければならないので、元々アップロードされたファイル名とは別名に変換する
-
confirm.php
からsend.php
には、元々アップロードされたファイル名と、一時ファイル名の2つを受け渡す -
send.php
では、受け渡された一時ファイル名を使って、メールにファイルを添付する。 - この際、メールを受信したユーザーには元々アップロードされたファイル名でダウンロード出来るように添付する
- サーバー容量節約のため、送信が完了したらファイルは消去する
- 確認画面でファイルを保管し、送信後に消す処理の為、確認画面で離脱するとファイルが残存する。
その為、一定時間毎にファイルを消去する
そして、上記要点を満たすのが以下となります。
index.php
ここでの変更点は少なく、11行目でファイルをアップロード出来るようにenctype="multipart/form-data"
を付け加え、21〜24行目で添付ファイルの入力項目を用意したのみです。
確認画面の有無に関係無く、ファイルアップロード機能があるフォームを作る時の基本ですね。
confirm.php
ここでは、FileOperator
クラス内のupload_file()
メソッドを通して、先述の要点のうち
-
index.php
から送信されたファイルは、PHPによって一時的にサーバー内に保管される - そのファイルを
send.php
で扱う為、confirm.php
の段階で、プログラム側で決めた一時ディレクトリに移動させる - この時、一時ディレクトリに保管するファイル名はユニークなもので無ければならないので、元々アップロードされたファイル名とは別名に変換する
の3つの処理を行っています。
(1〜5行目)
そして、upload_file()
から「元々のファイル名」と「一時保存のファイル名」がreturnされているので、それを<input type="hidden" />
に埋め込み
-
confirm.php
からsend.php
には、元々アップロードされたファイル名と、一時ファイル名の2つを受け渡す
の準備をしています。
(33・34行目)
file-operator.php(confirm.phpで呼ばれる箇所)
ファイル操作を行う責務を担うプログラムです。
手始めに、インスタンスが生成されるタイミングで、ファイルの一時保存を行うディレクトリの存在確認と、存在しない場合は作成を行います。
また、今回は作成したフォルダ内のファイルに対して、外部からのアクセスは不要な為、.htaccess
を利用して閲覧制限を設けました。
次に、先述のconfirm.php
で呼ばれる、upload_file()
です。
まず、ユニークなファイル名を実現するために、ファイル名を
- 現在時刻をマイクロタイム(100万分の1秒)で取得
- mt_rand()でランダムな数字を取得
- その2つを連結し、ファイル名とする
という方法で手法で決定しています。
(42〜44行目)
万全を期すなら、「ファイル名を生成->同一ファイル名の存在確認->存在する場合はファイル名を改めて生成->ファイル名の存在確認...」の再帰処理を入れてもいいかも知れませんね。
(100万分の1秒単位で同時アクセスがあり、更にランダムに決められた数字が偶然一致し、ファイルの拡張子も一緒、なんて事は無いと思って、僕は再帰処理は入れませんが)
send.php
最後に送信処理です。
ここでは先述の要点の内
-
send.php
では、受け渡された一時ファイル名を使って、メールにファイルを添付する。 - この際、メールを受信したユーザーには元々アップロードされたファイル名でダウンロード出来るように添付する
- サーバー容量節約のため、送信が完了したらファイルは消去する
- 確認画面でファイルを保管し、送信後に消す処理の為、確認画面で離脱するとファイルが残存する。
を実装しています。
まずファイルの添付に関しては、PHPMailer
のaddAtachment()
を使うと、「第一引数に添付したいファイルのパス」「第二引数に添付した時のファイル名」を指定するだけで、簡単に添付が行えます。
(22行目)
そして、24行目で送信が完了した後、27行目でFileOperator
クラスのdel_file()
メソッドを利用して、今回の添付ファイルの削除を行います。
さらに、過去アップロードされたものが残ってしまっている可能性を考慮して、同クラスのgarbage_collection()
で一定時間経過したファイルの削除を行っています。
file-operator.php(send.phpで呼ばれる箇所)
get_file_path()
とdel_file()
は、それぞれconfirm.php
からsend.php
に渡された一時ファイル名を引数として渡す事で、保存されたファイルのフルパスの取得を行ったり、削除を行うものです。
つまり、送信処理のあとにdel_file()
を呼び出せば、その送信に利用したファイルは消去出来ます。
加えて、garbage_collection();
では、残存している一時ファイルを全て取得し、ファイルの作成日時と現在日時を照らし合わせ、一定時間が経過した一時ファイルを削除しています。
まとめ
ここまでお読みいただきありがとうございました!
最後に、ファイルアップロード部分だけバリデーション加えたものを記載して今回は〆とさせていただきます。
Discussion