PAGE TOP


追加ボタンで画像フォームを増やしていく(データベース挿入・表示)

2017年7月9日Javascript, PHP

実装概要

前回、追加ボタンでフォームを増やしていく(表の処理)の3つ目に紹介した、画像を選択するとアップロード前にプレビューが表示される実装の続きになります。前回は、動的にフォームを追加していくという実装をやりましたが、今回は、それらの送信したPOSTデータやFILESデータのデータベースへ保存と、画像のアップロードの部分をやります。また、アップロードした画像の一覧を表示できるようにしたいと思います。画像一覧の編集については、次回に説明します。

データベース テーブルの作成

データベースには、POSTから渡されたテキストデータと、保存した画像の名前を登録します。また、user_idやpost_idというカラムを作りましたが、これは一般的に、ブログのような記事に画像を添付する際に、その記事のidと、その記事を書いたユーザーのidを関連付けるために、作りました。値は、定数として定義していますが、今回の実装だけ実現するならなくてもよいです。

CREATE TABLE img_data(
ID bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL,
post_id bigint(20) unsigned NOT NULL,
text text NOT NULL,
img varchar(200) NOT NULL,
date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (ID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

データベースの準備が完了したら、接続情報を記載したファイルを別途つくって、それをあとで読み込んで使うようにします。このあたりは、host dbname $user $password の部分は、環境にあわせて、変更してください。

【db.php】

<?php
 
function db_connect(){
	$dsn = 'mysql:host=localhost;dbname=xxxxxxx;charset=utf8';
	$user = 'xxxxxx';
	$password = 'xxxxxx';
	
	try{
		$dbh = new PDO($dsn, $user, $password);
		return $dbh;
	}catch (PDOException $e){
	    	print('Error:'.$e->getMessage());
	    	die();
	}
}
 
?>

フォームの作成

index.phpの説明は、前回のフォームの追加 テキスト&画像(アップロード前プレビューあり)を参考ください。

【index.php】

<script type="text/javascript">
$(function () {
        var num = 1;
        var view_count = document.querySelectorAll("div[id]").length;

        function imgView(n) {
            var reader = new FileReader();
            document.getElementById('file_' + n).onchange = function (e) {
                reader.addEventListener('load', function (e) {
                    $('#view_' + n).html('<img src="' + e.target.result + '" />');
                });
                reader.readAsDataURL(this.files[0]);
            }
        }

        imgView(num);

        $('button#add').click(function () {

          if(view_count ===5 ){
              $('#message').html('※ 追加フォームは' + view_count + 'つまでです。<br>');
            }else{

            num = num + 1;
            view_count = view_count + 1;

              var tr_form = '' +
                  '<tr>' +
                  '<td><input type="text" name="text[]"></td>' +
                  '<td><div  id="view_' + num + '"></div><input type="file" id="file_' + num + '" name="img[]" accept="image/*" /></td>' +
                  '</tr>';
              $(tr_form).appendTo($('table > tbody'));
              $('#reload').html('<input type="button" value="リロードする" onclick="window.location.reload();" /><br>');

              imgView(num);
          }
        });

});
</script>

<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="1048576">
    <table>
        <thead>
        <tr>
       <th>テキスト</th>
        <th>画像</th>
        </tr>
        </thead>
        <tbody>
        <tr>
            <td><input type="text" name="text[]"></td>
            <td>
                <div id="view_1"></div>
                <input type="file" id="file_1" name="img[]" accept="image/*">
            </td>
        </tr>
        </tbody>
        <tfoot>
        <tr>
            <td>
                <button id="add" type="button">追加</button><span id="reload"></span>
            </td>
        </tr>
        </tfoot>
    </table>
    <span id="message"></span>
    <input type="submit" name="send" value="送信">
</form>

データの挿入と画像の保存

upload.phpでのおおまかな処理の流れとしては、
・画像保存先のディレクトリがない場合は、img_xxx/年/月/日という形式でフォルダを作成する。画像は、アップロードする画像と、中サイズの画像、小サイズの画像を生成するために、3つのディレクトリを作成します。作成するファイルが置かれる親のディレクトリのパーミッションを書き込み可能にしておく必要があります。
・次に、ファイルサイズ等のエラーのチェックです。
・問題がなければ、アップロードします。
・アップロードした画像をもとに中サイズの画像を作成します。
・アップロードした画像をもとに小サイズの画像を作成します。
・最後に、テキストデータのPOSTデータと、保存した、ファイル名をデータベースへ保存して、view.phpへ飛ばします。

保存する画像の名前は、time()とmt_rand()を繋げた値をsha1()関数でハッシュ値に変換したものを使用することにしています。また各サイズに応じて、画像名のはじめに、小文字を添付しています。フルサイズ f 中サイズ m 小サイズ t のようにしていますが、開発者側の管理の都合なので、なくてもよいです。

【upload.php】

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>TEST</title>
</head>
<body>

<?php

$array_count = count($_POST['text']);

if($array_count > 5){
echo "5つまでです。";
exit;
}

define('IMAGES_DIR', dirname($_SERVER['SCRIPT_FILENAME']).'/');
define('MIDDLE_WIDTH', 200);
define('THUMBNAIL_WIDTH', 100);

error_reporting(E_ALL & ~E_NOTICE);


//保存先フォルダの作成 ※1 補足(下記)
$img_full = "img_full/".date("Y")."/".date("m")."/".date("d");
$img_med = "img_med/".date("Y")."/".date("m")."/".date("d");
$img_thu = "img_thu/".date("Y")."/".date("m")."/".date("d");

umask(0);  //0022回避 サーバーによって設定が異なる ※2 補足(下記)

if(!file_exists($img_full)){   //存在していないなら保存先フォルダを作る
  mkdir( $img_full, 0777, TRUE );
}

if(!file_exists($img_med)){
  mkdir( $img_med, 0777, TRUE );
}

if(!file_exists($img_thu)){
  mkdir( $img_thu, 0777, TRUE );
}

//エラー確認・バリテーション(サイズ)・ファイル形式
for($i=0; $i<$array_count; $i++)
{
  if ($_FILES['img']['error'][$i] != UPLOAD_ERR_OK) {
    echo "エラーが発生しました : ".$_FILES['img']['error'][$i] . "<br>";
  }

  if ($_FILES['img']['error'][$i]===2) {
    echo $_FILES['img']['name'][$i]." のファイルサイズが大きすぎます!<br>";
  }
}

//エラーありの場合処理を止める。
for($i=0; $i<$array_count; $i++)
{
  if ($_FILES['img']['error'][$i] != UPLOAD_ERR_OK) {
    exit;
  }
}

for($i=0; $i<$array_count; $i++)
{
  $imagesize[$i] = getimagesize($_FILES['img']['tmp_name'][$i]);

  switch($imagesize[$i]['mime']){
    case 'image/gif':
        $ext[$i] = '.gif';
        break;
    case 'image/jpeg':
        $ext[$i] = '.jpg';
        break;
    case 'image/png':
        $ext[$i] = '.png';
        break;
    default:
        echo $_FILES['img']['name'][$i]."は画像ファイル(GIF/JPEG/PNG) ではありません!!<br>";
       $ext[$i] = 0;
    }
}

//ファイル形式が異なる場合処理を止める
for($i=0; $i<$array_count; $i++)
{
  if ($ext[$i] === 0) {
    exit;
  }
}

//アップロード処理
for($i=0; $i<$array_count; $i++)
{
  $imageFileName[$i] = sha1(time().mt_rand()) . $ext[$i];

  $imageFilePath[$i] = IMAGES_DIR. $img_full . '/f' . $imageFileName[$i];   //fullサイズリネイム

  //echo $imageFilePath[$i]; //フルパス

  $rs[$i] = move_uploaded_file($_FILES['img']['tmp_name'][$i], $imageFilePath[$i]);

  if (!$rs[$i]) {
     echo 'no'. $i . 'could not upload!';
    exit;
  } 

$width = $imagesize[$i][0];
$height = $imagesize[$i][1];


//MIDDLE画像を作成、保存

  if ($width > MIDEEL_WIDTH) {
    // 元ファイルを画像タイプによって作る
    switch($imagesize[$i]['mime']){
    case 'image/gif':
        $srcImage[$i] = imagecreatefromgif($imageFilePath[$i]);
        break;
    case 'image/jpeg':
        $srcImage[$i] = imagecreatefromjpeg($imageFilePath[$i]);
        break;
    case 'image/png':
        $srcImage[$i] = imagecreatefrompng($imageFilePath[$i]);
        break;
    }

    // 新しいサイズを作る
    $middleHeight[$i] = round($height * MIDDLE_WIDTH / $width);

    // 縮小画像を生成
    $middleImage[$i] = imagecreatetruecolor(MIDDLE_WIDTH, $middleHeight[$i]);
    imagecopyresampled($middleImage[$i], $srcImage[$i], 0, 0, 0, 0, 200, $middleHeight[$i], $width, $height);
    
    // 縮小画像を保存する
    switch($imagesize[$i]['mime']){
    case 'image/gif':
        imagegif($middleImage[$i], IMAGES_DIR. $img_med .'/m'.$imageFileName[$i]);  //fullサイズリネイム
        break;
    case 'image/jpeg':
        imagejpeg($middleImage[$i], IMAGES_DIR. $img_med .'/m'.$imageFileName[$i]);
        break;
    case 'image/png':
        imagepng($middleImage[$i], IMAGES_DIR. $img_med .'/m'.$imageFileName[$i]);
        break;
    }

  } //if MIDDLE



//サルネイム
  if (MIDDLE_WIDTH > THUMBNAIL_WIDTH) {
    switch($imagesize[$i]['mime']){
    case 'image/gif':
        $srcImage[$i] = imagecreatefromgif($imageFilePath[$i]);
        break;
    case 'image/jpeg':
        $srcImage[$i] = imagecreatefromjpeg($imageFilePath[$i]);
        break;
    case 'image/png':
        $srcImage[$i] = imagecreatefrompng($imageFilePath[$i]);
        break;
    }
    $thumbHeight[$i] = round($height * THUMBNAIL_WIDTH / $width);
    $thumbImage[$i] = imagecreatetruecolor(THUMBNAIL_WIDTH, $thumbHeight[$i]);
    imagecopyresampled($thumbImage[$i], $srcImage[$i], 0, 0, 0, 0, 100, $thumbHeight[$i], $width, $height);

    switch($imagesize[$i]['mime']){
    case 'image/gif':
        imagegif($thumbImage[$i], IMAGES_DIR. $img_thu .'/t'.$imageFileName[$i]);  //tサイズリネイム
        break;
    case 'image/jpeg':
        imagejpeg($thumbImage[$i], IMAGES_DIR. $img_thu .'/t'.$imageFileName[$i]);
        break;
    case 'image/png':
        imagepng($thumbImage[$i], IMAGES_DIR. $img_thu .'/t'.$imageFileName[$i]);
        break;
    }
 } //if サルネイム

}

//データベースへの登録

define("USER_ID", 1);
define("POST_ID", 1);

require_once("db.php");
$dbh = db_connect();

try{

for($i=0; $i<$array_count; $i++)
{
   $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   $sql = "INSERT INTO img_data(user_id, post_id, text, img) VALUES(:user_id, :post_id, :text, :img)";
   $stmt = $dbh->prepare($sql);
   $stmt->bindValue(':user_id',USER_ID);
   $stmt->bindValue(':post_id', POST_ID);
   $stmt->bindValue(':text',$_POST['text'][$i]);
   $stmt->bindValue(':img', $imageFileName[$i]);
   $stmt->execute();
}

}catch (PDOException $e){
   print('Error:'.$e->getMessage());
   die();
}

header('Location: http://sample.com/view.php');
exit;
?>

</body>
</html>

※1 補足
date()関数に注意することは、特に、ローカル開発しているかたでXamppを使っている方は、初期設定のままだと、タイムゾーンが、日本ではないので、遅れて表示されます。特に、今回の実装では、リアルタイムと、データベースの時間を一致させないと、アップデートファイルを表示するときに不具合が生じますので、タイムゾーン設定をしてください。php.ini ファイルを開いて、 timezoneというワードで検索してください。date.timezone= の場所が2箇所あるので、2つ目を date.timezone=Asia/Tokyo と修正すれば、正しく表示されるようになります。 

※2 補足
mkdir()関数でパーミッションを設定する際に、0777にしても、実際確認してみると、755になっていて、繁栄されない場合がある。この場合は、もともとサーバーの設定で、umaskの値が、0022となってる場合があるからである(このあたりはレンタルサーバーによって異なる)。仕組みとしては、0777-0022=0755にってしまうという意味。
unaskの値は、SSH接続で、$umask 入力すれば、値が表示されるので確認することができる。
また、この場合は、umaskの値をゼロに書き換えれば、マイナスしても、設定した値になるので、mkdirの前に、値をゼロに設定する。

データと画像を表示する

【view.php】

<?php
require_once("db.php");
$dbh = db_connect();

try{
   $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   $sql = "SELECT * FROM img_data WHERE user_id = 1 AND post_id = 1";
   $stmt = $dbh->query($sql);
   $data = $stmt->fetchAll();
   
}catch (PDOException $e){
   print('Error:'.$e->getMessage());
   die();
}
?>

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>TEST</title>
    <style type="text/css">
        img {
            margin: 0 5px 5px 0;
            max-width: 160px;
            vertical-align: bottom;
        }
    </style>
</head>
<body>

<?php
foreach($data as $key){
  list($year, $month, $day) = preg_split('/[-: ]/', $key['date']);  
  echo $key['text'].'<br>';
  echo '<a href="http://sample.com/img_full/'. $year  .'/'. $month  .'/'. $day  .'/f'. $key['img'].'"><img src="http://sample.com/f_004/img_med/'. $year  .'/'. $month  .'/'. $day  .'/m'. $key['img'].'"/></a><br>'  ;
}
?>

</body>
</html>