概要と目標
添付ファイルをアップロード
出来るようになろう。
投稿フォームに添付された画像の、
アップロード方法を学習しましょう。
投稿フォームに添付された画像の、
アップロード方法を学習しましょう。
データベースは画像自体を保存することが出来ない。
そこで画像のファイル名をデータベースに保存する。
ファイル名があれば、img要素で画像を生成できる。
まずは、画像のファイル名を格納するため、テーブルにフィールドを追加する。
今回は、「phpMyAdmin」というツールを使って、フィールドを追加してみよう。
| 名前 | データ型 | 長さ/値 | NULL |
|---|---|---|---|
post_image |
VARCHAR | 255 | On |
画像を添付するには、<input type="file">を使う。
ただし、ファイルを添付するにはform要素に、enctype="multipart/form-data"属性を指定する必要がある。
form要素に、とenctype属性を指定し、
ファイルを添付するコントロール部品を追加する
<form action="add.php" method="post" enctype="multipart/form-data">
<dl>
<dt><label for="title">記事のタイトル</label></dt>
<dd>
<input type="text" id="title" name="title">
</dd>
<dt><label for="category_id">カテゴリー</label></dt>
<dd>
<select name="category_id" id="category_id">
<?php foreach($result as $row) : ?>
<option value="<?php echo h($row['id']); ?>"><?php echo h($row['category_name']); ?></option>
<?php endforeach; ?>
</select>
</dd>
<dt><label for="content">記事の内容</label></dt>
<dd>
<textarea name="content" id="content" cols="30" rows="10"></textarea>
</dd>
<dt><label for="post_image">画像</label></dt>
<dd>
<input type="file" id="post_image" name="post_image">
</dd>
</dl>
<p><input type="hidden" name="token" value="<?php echo h($_SESSION['token']); ?>"></p>
<p><input type="submit" value="投稿"></p>
</form>
$_FILESに格納される。
添付されたファイルの情報は、$_FILESというスーパーグローバル変数に格納される。
$_FILES ・・・ 添付ファイルの情報が格納されているスーパーグローバル変数$_FILES[name属性の値][キー]
【キー】
"name": ファイル名が格納されている
"type": ファイルのMIMEタイプが格納されている(この値は信用できない)
"tmp_name": 一時的に保存されているファイルパス
"error": アップロードに関するエラーコードが格納されている
(0 : UPLOAD_ERR_OK (エラーなし))
詳細はPHPマニュアルを参照
$_FILESで受け取った情報をprint_r関数で出力$stmt->execute();をコメントにしておく
<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// 投稿ボタンが押されたかをチェック
if ( $_SERVER['REQUEST_METHOD'] !== 'POST') {
// ダイレクトでアクセスされた時
header('Location: post.php');
exit();
}
// CSRF対策 ・・・ トークンのチェック
check_token();
// 添付ファイルの配列を出力
print_r($_FILES['post_image']);
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成
$sql = 'INSERT INTO posts (title, category_id, content, created) VALUES(?, ?, ?, now())';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, $_POST['title'], PDO::PARAM_STR);
$stmt->bindValue(2, (int)$_POST['category_id'], PDO::PARAM_INT);
$stmt->bindValue(3, $_POST['content'], PDO::PARAM_STR);
// ステートメントを実行
// $stmt->execute();
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
ファイルは基本的になんでも添付できる。
従って画像以外のファイルが添付されている可能性もあるので、
きちんと画像ファイルかを確認する。
$_FILE[name属性の値]['type']でも形式は確認できるが、
この値はあまり信用出来ないので、exif_imagetype関数でチェックするほうがよい。
exif_imagetype関数 ・・・ 画像の形式を取得する関数exif_imagetype(画像ファイル)
| 値 | 定数 |
|---|---|
1 |
IMAGETYPE_GIF |
2 |
IMAGETYPE_JPEG |
3 |
IMAGETYPE_PNG |
詳細はPHPマニュアルを参照
print_r($_FILES['post_image']);をコメントにし、<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// 投稿ボタンが押されたかをチェック
if ( $_SERVER['REQUEST_METHOD'] !== 'POST') {
// ダイレクトでアクセスされた時
header('Location: post.php');
exit();
}
// CSRF対策 ・・・ トークンのチェック
check_token();
// 添付ファイルの配列を出力
// print_r($_FILES['post_image']);
// 添付ファイルが存在し、かつエラーが無いかをチェック
if ( isset($_FILES['post_image']) && $_FILES['post_image']['error'] === UPLOAD_ERR_OK ) {
// 添付ファイルがあり、エラーもない時
}
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成
$sql = 'INSERT INTO posts (title, category_id, content, created) VALUES(?, ?, ?, now())';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, $_POST['title'], PDO::PARAM_STR);
$stmt->bindValue(2, (int)$_POST['category_id'], PDO::PARAM_INT);
$stmt->bindValue(3, $_POST['content'], PDO::PARAM_STR);
// ステートメントを実行
// $stmt->execute();
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
$exif_imagetype関数で、添付ファイルの形式をチェックし、exit関数で処理を終了
<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// 投稿ボタンが押されたかをチェック
if ( $_SERVER['REQUEST_METHOD'] !== 'POST') {
// ダイレクトでアクセスされた時
header('Location: post.php');
exit();
}
// CSRF対策 ・・・ トークンのチェック
check_token();
// 添付ファイルの配列を出力
// print_r($_FILES['post_image']);
// 添付ファイルが存在し、かつエラーが無いかをチェック
if ( isset($_FILES['post_image']) && $_FILES['post_image']['error'] === UPLOAD_ERR_OK ) {
// 添付ファイルがあり、エラーもない時
// ファイルタイプを取得
$file_type = exif_imagetype($_FILES['post_image']['tmp_name']);
// 画像ファイルかをチェック
if ($file_type != IMAGETYPE_GIF && $file_type != IMAGETYPE_JPEG && $file_type != IMAGETYPE_PNG) {
// 許可していないファイル形式ならエラー
echo '画像は「gif」、「jpeg」、「png」形式で指定して下さい。';
exit();
}
}
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成
$sql = 'INSERT INTO posts (title, category_id, content, created) VALUES(?, ?, ?, now())';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, $_POST['title'], PDO::PARAM_STR);
$stmt->bindValue(2, (int)$_POST['category_id'], PDO::PARAM_INT);
$stmt->bindValue(3, $_POST['content'], PDO::PARAM_STR);
// ステートメントを実行
// $stmt->execute();
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
画像は一時ファイルとしてアップロードされている。
これを正式なフォルダに移動することがアップロードとなる。
ただし、同じファイル名の画像が添付された時は、
後からアップロードされた時に上書きされてしまうため、
ファイル名が重複しない工夫をする必要がある。
move_uploaded_file関数 ・・・ アップロードされたファイルを移動する関数move_uploaded_file(一時ファイル, 移動先)
詳細はPHPマニュアルを参照
move_uploaded_file関数で、ファイルを移動<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// 投稿ボタンが押されたかをチェック
if ( $_SERVER['REQUEST_METHOD'] !== 'POST') {
// ダイレクトでアクセスされた時
header('Location: post.php');
exit();
}
// CSRF対策 ・・・ トークンのチェック
check_token();
// 添付ファイルの配列を出力
// print_r($_FILES['post_image']);
// 添付ファイルが存在し、かつエラーが無いかをチェック
if ( isset($_FILES['post_image']) && $_FILES['post_image']['error'] === UPLOAD_ERR_OK ) {
// 添付ファイルがあり、エラーもない時
// ファイルタイプを取得
$file_type = exif_imagetype($_FILES['post_image']['tmp_name']);
// 画像ファイルかをチェック
if ($file_type != IMAGETYPE_GIF && $file_type != IMAGETYPE_JPEG && $file_type != IMAGETYPE_PNG) {
// 許可していないファイル形式ならエラー
echo '画像は「gif」、「jpeg」、「png」形式で指定して下さい。';
exit();
}
// 画像のアップロード先
$file_dir = '../upload/';
// ファイル名を生成 (ファイル名の前に現在の日時を付加)
$file_name = date('YmdHis') . '-' . $_FILES['post_image']['name'];
// ファイルを一時フォルダから移動
move_uploaded_file($_FILES['post_image']['tmp_name'], $file_dir. $file_name);
}
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成
$sql = 'INSERT INTO posts (title, category_id, content, created) VALUES(?, ?, ?, now())';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, $_POST['title'], PDO::PARAM_STR);
$stmt->bindValue(2, (int)$_POST['category_id'], PDO::PARAM_INT);
$stmt->bindValue(3, $_POST['content'], PDO::PARAM_STR);
// ステートメントを実行
// $stmt->execute();
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
INSERT文に画像のフィールドを追加し、
$stmt->execute();のコメントを外す
<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// 投稿ボタンが押されたかをチェック
if ( $_SERVER['REQUEST_METHOD'] !== 'POST') {
// ダイレクトでアクセスされた時
header('Location: post.php');
exit();
}
// CSRF対策 ・・・ トークンのチェック
check_token();
// 添付ファイルの配列を出力
// print_r($_FILES['post_image']);
// ファイル名の初期値
$file_name = '';
// 添付ファイルが存在し、かつエラーが無いかをチェック
if ( isset($_FILES['post_image']) && $_FILES['post_image']['error'] === UPLOAD_ERR_OK ) {
// 添付ファイルがあり、エラーもない時
// ファイルタイプを取得
$file_type = exif_imagetype($_FILES['post_image']['tmp_name']);
// 画像ファイルかをチェック
if ($file_type != IMAGETYPE_GIF && $file_type != IMAGETYPE_JPEG && $file_type != IMAGETYPE_PNG) {
// 許可していないファイル形式ならエラー
echo '画像は「gif」、「jpeg」、「png」形式で指定して下さい。';
exit();
}
// 画像のアップロード先
$file_dir = '../upload/';
// ファイル名を生成 (ファイル名の前に現在の日時を付加)
$file_name = date('YmdHis') . '-' . $_FILES['post_image']['name'];
// ファイルを一時フォルダから移動
move_uploaded_file($_FILES['post_image']['tmp_name'], $file_dir. $file_name);
}
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成
$sql = 'INSERT INTO posts (title, category_id, content, post_image, created) VALUES(?, ?, ?, ?, now())';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, $_POST['title'], PDO::PARAM_STR);
$stmt->bindValue(2, (int)$_POST['category_id'], PDO::PARAM_INT);
$stmt->bindValue(3, $_POST['content'], PDO::PARAM_STR);
$stmt->bindValue(4, $file_name, PDO::PARAM_STR);
// ステートメントを実行
$stmt->execute();
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
img要素を作ればいい。
アップロードした画像を表示するには、データベースに保存した画像のファイル名を使って、
img要素を作ればよい。
img要素で画像を表示する
<h1><?php echo h($result['title']); ?></h1>
<?php if( !empty($result['post_image']) ) : ?>
<figure>
<img src="upload/<?php echo h($result['post_image']); ?>" alt="<?php echo h($result['title']); ?>">
</figure>
<?php endif; ?>
<ul>
<li>公開日: <time datetime="<?php echo h($result['created']); ?>"><?php echo h(date('Y年m月d日', strtotime($result['created']))); ?></time></li>
<li>カテゴリ: <?php echo h($result['category_name']); ?></li>
</ul>
<p>
<?php echo nl2br(h($result['content']), false); ?>
</p>
編集フォームにも添付ファイルのコントロール部品を設置する。
ただし、添付ファイルのコントロール部品には、初期値を設定できない。
従って、画像がある記事は、img要素で画像を表示し、
ファイル名を<input type="hidden">に埋め込み、「update.php」に送る。
form要素にenctype属性を指定し、添付ファイルのコントロール部品を追加img要素で画像を表示し、
<input type="hidden">で、<form action="update.php" method="post" enctype="multipart/form-data">
<dl>
<dt><label for="title">記事のタイトル</label></dt>
<dd>
<input type="text" id="title" name="title" value="<?php echo h($result['title']); ?>">
</dd>
<dt><label for="category_id">カテゴリー</label></dt>
<dd>
<select name="category_id" id="category_id">
<?php foreach($categories as $category) : ?>
<?php
$selected = '';
// ループで出力するカテゴリと、データベースから取得したカテゴリが一致するかチェック
if ($category['id'] == $result['category_id']) {
// 一致した場合
$selected = ' selected';
}
?>
<option value="<?php echo h($category['id']); ?>"<?php echo h($selected); ?>><?php echo h($category['category_name']); ?></option>
<?php endforeach; ?>
</select>
</dd>
<dt><label for="content">記事の内容</label></dt>
<dd>
<textarea name="content" id="content" cols="30" rows="10"><?php echo h($result['content']); ?></textarea>
</dd>
<dt><label for="post_image">画像</label></dt>
<dd>
<?php if( !empty($result['post_image']) ) : ?>
<img src="../upload/<?php echo h($result['post_image']); ?>" width="240" alt="<?php echo h($result['title']); ?>"><br>
<?php endif; ?>
<input type="file" id="post_image" name="post_image">
</dd>
</dl>
<p><input type="hidden" name="id" value="<?php echo h($result['id']); ?>"></p>
<p><input type="hidden" name="post_image" value="<?php echo h($result['post_image']); ?>"></p>
<p><input type="hidden" name="token" value="<?php echo h($_SESSION['token']); ?>"></p>
<p><input type="submit" value="変更"></p>
</form>
画像は必ず更新されるとは限らない。
更新する画像が添付されていた場合は、形式などのチェックをして更新するが、
添付されていない場合で、編集前から画像が設定されている場合は注意が必要。
編集前から設定されている画像をUPDATE文で指定しないと、
画像が消えてしまう。
UPDATE文に追加する
<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// 変更ボタンが押されたかをチェック
if ( $_SERVER['REQUEST_METHOD'] !== 'POST') {
// ダイレクトでアクセスされた時
header('Location: index.php');
exit();
}
// CSRF対策 ・・・ トークンのチェック
check_token();
// ファイル名の初期値
$file_name = $_POST['post_image'];
// 添付ファイルが存在し、かつエラーが無いかをチェック
if ( isset($_FILES['post_image']) && $_FILES['post_image']['error'] === UPLOAD_ERR_OK ) {
// 添付ファイルがあり、エラーもない時
// ファイルタイプを取得
$file_type = exif_imagetype($_FILES['post_image']['tmp_name']);
// 画像ファイルかをチェック
if ($file_type != IMAGETYPE_GIF && $file_type != IMAGETYPE_JPEG && $file_type != IMAGETYPE_PNG) {
// 許可していないファイル形式ならエラー
echo '画像は「gif」、「jpeg」、「png」形式で指定して下さい。';
exit();
}
// 画像のアップロード先
$file_dir = '../upload/';
// ファイル名を生成 (ファイル名の前に現在の日時を付加)
$file_name = date('YmdHis') . '-' . $_FILES['post_image']['name'];
// ファイルを一時フォルダから移動
move_uploaded_file($_FILES['post_image']['tmp_name'], $file_dir. $file_name);
}
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成
$sql = 'UPDATE posts SET title=?, category_id=?, content=?, post_image=? WHERE id = ?';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, $_POST['title'] , PDO::PARAM_STR);
$stmt->bindValue(2, (int)$_POST['category_id'] , PDO::PARAM_INT);
$stmt->bindValue(3, $_POST['content'] , PDO::PARAM_STR);
$stmt->bindValue(4, $file_name , PDO::PARAM_STR);
$stmt->bindValue(5, (int)$_POST['id'] , PDO::PARAM_INT);
// ステートメントを実行
$stmt->execute();
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
添付ファイルは必ず形式をチェックし、一時ファイルを正式なフォルダに移動する。