【PHP+MySQL】恐竜画像登録＆画像表示にトライ！！！（恐竜シリーズプチ最終回）

###はじめに

[改良恐竜登録ツール(PHP+MYSQL版)]
(https://qiita.com/knowledge87sun/items/aea1c603cab586b7bcf5)
でも恐竜登録ツールを作成した。
しかし、何かが足りない。
恐竜登録と言えば、画像！！！

[前回](https://qiita.com/knowledge87sun/items/28136aaf2e674e0d290d)の記事で、DBに保存した画像の表示方法をまとめた。

今回は今までの内容を結集した恐竜画像登録&画像表示プログラムを作成してみた。

###1.プログラム概要

####機能１．登録済み恐竜画像を表示
![dino_image1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494408/8ae8a124-0036-4760-e42a-c3e9cb758c48.jpeg)

例）7のリンクをクリックすると、インドミナスレックスの画像を表示。

![dino_image2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494408/bddea4c7-e8f9-0646-b971-2c202a064504.jpeg)


####機能２．恐竜の画像登録

（1）カテゴリーを確定後、登録したい恐竜(ティラノサウルス)を選択。
登録恐竜一覧には、画像登録済みの恐竜が表示されている。


![dino_image3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494408/f2b47980-d31e-66c5-d33f-c6eb4d21588f.jpeg)

（2）ファイル選択ダイアログで、登録したい画像を選択して確定ボタンを押す。
(tirano_mini.jpg(選択した画像)が表示される。）


![dino_image4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494408/afc30923-3370-67c2-675a-7d6109dc0738.jpeg)

(3)登録が完了すると、登録済みの恐竜一覧にリンクが表示される。
![dino_image5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494408/8ad5bd82-2395-d686-fd8f-1da148a1cebb.jpeg)

(4)リンクをクリックすると登録された画像が表示される。
![dino_image6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494408/9dcaf39e-6edd-3f62-721b-ad5d6988106f.jpeg)


###2.実際のコード


(1)画像格納用のテーブル(tbl_dinoimg)の作成
id:恐竜のID
ext:画像ファイルのmimeタイプ
img:画像ファイルをバイナリデータにしたもの


```sql

create table tbl_dinoimg (
 id integer,
 img mediumblob,
 ext varchar(10),
 primary key(id)
);
```

```php:common_dino2_image.php
<?php 

  function NewPdo($dbname)
  {
    $dsn = 'mysql:dbname='.$dbname.';host=localhost:3308;charset=utf8mb4';
    $user = 'root';
    $password = 'root123';
    $dbh = new PDO($dsn, $user, $password);
    return $dbh;
  
  }

  function Checkdino($db0,$qry0,$val1,$type1)
  {
    $result3=$db0->prepare($qry0);
    //パラメータをセット
    $result3->bindparam(1,$val1);
    $result3->bindparam(2,$type1);
    $result3->execute();
    //検索結果を配列に格納する
    $select_data=$result3->fetch();
    return $select_data;
  }
  //クエリ実行して得られる全データ(パラメータ)
  function get_SelectTypeData($db0,$query0,$val1)
  {
    $result_a=$db0->prepare($query0);
    $result_a->bindparam(1,$val1);
    $result_a->execute();
    return $result_a;
  }
  //クエリ実行して得られる全データ
  function get_AllData($db0,$query0)
  {
    $result_a=$db0->prepare($query0);
    $result_a->execute();
    return $result_a;
  }
  //$str1:検索対象文字列,$str_mark:探したい文字: 戻り値、探したい文字より前にある文字列
  function get_BeforeMarkString($str1,$str_mark)
  {
    $idx0=strpos($str1, $str_mark);
    $retstr=substr($str1,0,$idx0);
    return $retstr;
  }

  //$str1:検索対象文字列,$str_mark:探したい文字、探したい文字より後にある文字列
  function get_AfterMarkString($str1,$str_mark)
  {
    $idx0=strpos($str1, $str_mark);
    $retstr=substr($str1,$idx0+1);
    return $retstr;
  }
 ?>  

```

```php:start_dino_image.php

<!DOCTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>恐竜画像登録</title>
<body>
<h1>恐竜画像登録!!</h1>

<?php 

  require "common_dino2_image.php";
  //画像フォーム部分
  $dbname='db_dino';
  $dbh=NewPdo($dbname);
  
  $query_type0 = "SELECT DISTINCT type0 from tbl_dino";
 ?>

  <!-- tbl_dinoに登録されている恐竜カテゴリーと一覧を表示する部分 -->
  <form action="start_dino_image.php" method="post">
カテゴリー:
  <?php $result_a=get_AllData($dbh,$query_type0); 
  $cate0=isset($_POST['dtype01'])?htmlspecialchars($_POST['dtype01']):null;

   ?> 
  <select name="dtype01" style="width: 100px">
    <?php while($select_data=$result_a->fetch(PDO::FETCH_ASSOC)){
      //$cate0が$select_data['type0']と等しい場合
      if($cate0 == $select_data['type0']){ 
        echo "<option selected>".$select_data['type0']."</option>";
      //$cate0が$select_data['type0']と等しくない場合
      }else{
        echo "<option>".$select_data['type0']."</option>";
      }
    }  
    ?>
  </select>
  <input type="submit" name = "btn2" value="確定">
  </form>

<?php 

  $dbname='db_dino';
  $dbh=NewPdo($dbname);
  $query_select_type0 = "SELECT dname0 from tbl_dino where type0 = ?";
  $result3=get_SelectTypeData($dbh,$query_select_type0,$cate0);

 ?>
  <form action="start_dino_image.php" enctype="multipart/form-data" method="post">
対象恐竜：
  <select name="dname" style="width: 280px">
    <?php $dino0=isset($_POST['dname'])?htmlspecialchars($_POST['dname']):null; ?>
    <?php while($select_data=$result3->fetch(PDO::FETCH_ASSOC)){
      echo "<option>".$select_data['dname0']."</option>";
    }
    ?>
  </select><br>
  <label>画像/動画アップロード</label>
  <input type="file" name="upfile">
  <input type="hidden" name="btn3" value="<?=$cate0?>">
  <input type="submit" value="確定">
  </form>


<?php 

  //ファイルアップロードがあったとき
  try{
    $dbname='db_dino';
    $dbh=NewPdo($dbname);
    //$pdo = new PDO("mysql:host=127.0.0.1;dbname=mediatest;charset=utf8", $user, $pass);
    if (isset($_FILES['upfile']['error']) && is_int($_FILES['upfile']['error']) && $_FILES['upfile']['name'] !== ""){
    //エラーチェック
      switch ($_FILES['upfile']['error']) {
        case UPLOAD_ERR_OK: // OK
          break;
        case UPLOAD_ERR_NO_FILE:   // 未選択
          throw new RuntimeException('ファイルが選択されていません', 400);
        case UPLOAD_ERR_INI_SIZE:  // php.ini定義の最大サイズ超過
          throw new RuntimeException('ファイルサイズが大きすぎます', 400);
        default:
          throw new RuntimeException('その他のエラーが発生しました', 500);
      }
      
  
      //拡張子を見る
      $tmp = pathinfo($_FILES['upfile']['name']);
      $extension = $tmp["extension"];
      if($extension === "jpg" || $extension === "jpeg" || $extension === "JPG" || $extension === "JPEG"){
        $ext1 = "image/jpeg";
      }
      elseif($extension === "png" || $extension === "PNG"){
        $ext1 = "image/png";
      }
      else{
        echo "非対応ファイルです．<br/>";
        echo ("<a href=\"start_dino_image.php\">戻る</a><br/>");
        exit(1);
      }
      //カテゴリーと恐竜名からidを探す
      $sql_select="SELECT * FROM tbl_dino WHERE dname0=? and type0=?";
      $dname1=isset($_POST['dname'])?htmlspecialchars($_POST['dname']):null;
      $type1=isset($_POST['btn3'])?htmlspecialchars($_POST['btn3']):null;
      $select_data1=Checkdino($dbh,$sql_select,$dname1,$type1);
      $dino_id=$select_data1['id'];

      //画像・動画をバイナリデータにする．
      $raw_data = file_get_contents($_FILES['upfile']['tmp_name']);
    
    }
    $dbname='db_dino';
    $dbh=NewPdo($dbname);
    
    $sql_del = "DELETE FROM tbl_dinoimg WHERE id=?";
    //画像・動画をDBから削除
    $result1=$dbh->prepare($sql_del);
    //パラメータをセット
    $result1->bindparam(1,$dino_id);
    $result1->execute();

    //画像・動画をDBに格納．
    $sql_ist = "INSERT INTO tbl_dinoimg(id, img, ext) VALUES (?,?,?)";
    $result1=$dbh->prepare($sql_ist);
    //パラメータをセット
    $result1->bindparam(1,$dino_id,PDO::PARAM_INT);
    $result1->bindparam(2,$raw_data,PDO::PARAM_LOB);
    $result1->bindparam(3,$ext1,PDO::PARAM_STR);
    $result1->execute();
    echo $dino_id."<br>";
  }

  catch(PDOException $e){
    echo("<p>500 Inertnal Server Error</p>");
    exit($e->getMessage());
  }

 ?>


<?php  
  //tbl_dinoimgに登録されている恐竜idと名前を表示する。
  $sql_idall="SELECT T1.id,T1.dname0,T1.type0 FROM tbl_dino T1";
  $sql_idall = $sql_idall." INNER JOIN tbl_dinoimg T2 ON T1.id=T2.id ORDER BY T2.id";

  $dbname='db_dino';
  $dbh=NewPdo($dbname);

  $result1=get_AllData($dbh,$sql_idall);
  echo "<br><h4>登録恐竜一覧</h4><br>";
  while ($row = $result1 -> fetch(PDO::FETCH_ASSOC)){
    $id=$row['id'];
 ?> 
  <!-- idと恐竜名とカテゴリー名 -->
    <a href="dino_display_image.php?id=<?=$id?>"><?=$id?></a>:<?= $row['dname0'] ?>(<?= $row['type0'] ?>)
<?php 
    echo ("\n"."<br/>");
  }
  $dbh=null;
  
 ?>

</body>
</html>
```

実はこの格納した画像を表示する事が超大変だった。
(前回の記事の部分)
よくある下記のコードでは、画面に表示されるのはどこまでも深い
黒一色の画面だったのだ。

```php

header("Content-Type: image/jpeg");
echo ($row['img']);
```

そのうちにbase64というキーワードを見つけることになる。
１．DBから画像バイナリデータを取得
２．base64でエンコード
３.２を画像のmimeタイプで表示
という手順があることが分かった



```php:dino_display_image.php
<?php 
  if(isset($_GET['id']) && $_GET['id'] !== ""){
    $id = $_GET['id'];
  }
  else{
    header("Location: start_dino_image.php");
  }
  try {
    require "common_dino2_image.php";
    $dbname='db_dino';
    $dbh = NewPdo($dbname);
    $sql_select = "SELECT ext,img FROM tbl_dinoimg WHERE id = ?";
    $result1=$dbh->prepare($sql_select);
    //パラメータをセット
    $result1->bindparam(1,$id,PDO::PARAM_INT);
    $result1->execute();
    $row = $result1 -> fetch(PDO::FETCH_ASSOC);
    //取得した画像バイナリデータをbase64で変換。
    $img = base64_encode($row['img']); 
 ?>
	<!-- エンコードした情報をimgタグに表示 -->
    <img src="data:<?php echo $row['ext'] ?>;base64,<?php echo $img; ?>"><br>
    <a href="start_dino_image.php">戻る</a>

<?php 


  }
  catch (PDOException $e) {
    echo("<p>500 Inertnal Server Error</p>");
    exit($e->getMessage());
  }
 ?>

```

###参考URL

[PHPでbase64の使い方を知ろう！サンプルコードで速攻理解](https://www.sejuku.net/blog/76450)

###今後に向けて

PHPでは一応恐竜シリーズの区切りがついた。
他のプログラミング言語でも、このシリーズにトライしたいと考えている。