pen00log

しがないwebエンジニアです

このブログについて

はじめまして、ペンまるです。

 

未経験からエンジニアへの転職を目指して勉強中です。
勉強したこと、その中でハマったことを書いていきます。


・自己紹介

工学部を卒業後、新卒で証券会社の営業に。
その後CADオペレーターに転職し現在に至ります。
エンジニアとしての転職時期としては
2019年8月1日付けでの採用を目標に、
2019年4月ごろから転職活動を始める予定です。
なんとなく手を付けた言語がPHPだったからWeb系でバックエンドに
いけたらいいなぁとぼんやり考え中。


・プログラミングの勉強のこと

2018.8~
progateでHTML/CSS,PHP,SQLの学習Ⅰを一通りやってみる
2018.9~
PHP,Mysqlを使って簡易掲示板の作成
2018.10~
簡易掲示板に、メール認証付き会員登録・ログイン機能、
画像・動画投稿機能を実装←今ここ!


エンジニアや同じようにエンジニアを目指している方々と交流できればと思っているので
お気軽にコメント等してください♪

 

【PHP】画像のサムネイル化について

PHPでの画像のサムネイル作成の手順について、 一見何をしているのかさっぱり分からなかったので 自分なりに噛み砕いてみましたメモです。

最初「サムネイル表示されるようにしたいな」と思った時は なんとなく、元画像と幅と高さを引き数に入れておけば その通りに画像を伸縮してくれる関数とか用意されてるんじゃないの~ とか勝手に思っていたんですけどそんなに甘くありませんでした・・笑

コードを載せる前に、サムネイルができるまでの工程を記しておきます。

①元画像をサーバー上にアップする
②拡張子の確認
③元画像からサムネイルの幅と高さを算出
④算出した幅、高さの真っ黒な画像をサーバー上に作成
⑤真っ黒な画像に、元画像を貼り付ける


プリンターで写真を現像するとき、欲しい大きさの用紙を用意して(この場合は真っ白な紙だけど) 画像を印刷しますよね?イメージはそれと同じです!

では全体のコードを載せます。前提として、

<form method="POST" action="<?php print($_SERVER['PHP_SELF']) ?>"enctype="multipart/form-data">
<input type="file" name="upfile">
<input type="submit" name="btn" value="送信">
</form>

こんなかんじのフォームとファイル名(upfile)で画像ファイルが送られているとします。

//拡張子を見る
<?php
$tmp = pathinfo($_FILES["upfile"]["name"]);
$extension = $tmp["extension"];
if ($extension === "jpg" || $extension === "jpeg" || $extension === "JPG" || $extension === "JPEG") {
         $extension = "jpeg";
    } elseif ($extension === "png" || $extension === "PNG") {
         $extension = "png";
    } elseif ($extension === "gif" || $extension === "GIF") {
         $extension = "gif";
    } else {
         echo "非対応ファイルです.<br/>";
         exit(1);//プログラムを終了する
    }

//画像データのサムネイル作成
if ($extension === "jpeg" || $extension === "png" || $extension === "gif") {
        //$_FILES['upfile']['mime']の値はブラウザ側で偽装可能なので
        // MIMEタイプを自前でチェックする
        //例:jpegファイルの場合
        //getimagesize():$info[0]=画像の幅、$info[0]=画像の幅、$info[2]=2(IMAGETYPE_JPEG),$info['mine']=image/jpeg
        if (!$info = @getimagesize($_FILES['upfile']['tmp_name'])) { //falseのときはエラーメッセージを出したいので、@でエラー制御
                throw new RuntimeException('有効な画像ファイルを指定してください', 400);
            }
        $array = array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);
        if (!in_array($info[2], $array, true)) {
                throw new RuntimeException('未対応の画像形式です', 400);
            }
            
        $create = str_replace('/', 'createfrom', $info['mime']); //image/jpeg→imagecreatefromjpeg
        $output = str_replace('/', '', $info['mime']); //image/jpeg→imagejpeg
         
        //サムネイル画像の幅、高さを算出
        //長いほうの辺を120ピクセルとし、もう一方の辺をそれにあわせて縦横比が変わらないように伸縮
        if ($info[0] >= $info[1]) {
        $dst_w = 120;
        //ceil():数値を整数値へ切り上げて返す
        $dst_h = ceil(120 * $info[1] / max($info[0], 1)); //画像サイズの変更
        } else {
            $dst_w = ceil(120 * $info[0] / max($info[1], 1)); 
            $dst_h = 120;
        }
    //$create = imagecreatefromjpeg('ファイル名'):'ファイル名'のデータをもとにサーバー上に画像を作成
    if (!$src = @$create($_FILES['upfile']['tmp_name'])) {
            throw new RuntimeException('画像リソースの生成に失敗しました', 500);
        }
    //imagecreatetruecolor():指定された幅と高さの真っ黒の画像を作成
    $dst = imagecreatetruecolor($dst_w, $dst_h);
    //真っ黒の画像($dst)に乗せたい画像($src)を載せる作業
    imagecopyresampled($dst, $src, 0, 0, 0, 0, $dst_w, $dst_h, $info[0], $info[1]);
    //image/jpeg():画像の出力
    $output($dst); 
    //念のため?メモリを開放しておく
    imagedestroy($src);
    imagedestroy($dst);
}
>


2~14行目
pathinfo()を使って拡張子を調べて、表記を揃えています。
今回のようにサムネイルを出力するだけの場合、別に揃える必要は無いのですが、 データベースをに一旦保存して表示させるためにこの手順を踏んでいます。

18~31行目
必要な情報を準備しています。
元となるjpeg画像からサーバー上にデータをを生成するimagecreatefromjpeg()や、 画像を出力するimagejpeg()はそれぞれ拡張子ごとに 関数名が変わるのですが、if文で分岐すると大変なのでinfo()の返り値である$info['mime']をうまく使って 関数名をそれぞれ$create、$outputに保存しています。

33~42行目
サムネイル後の画像の大きさを決定しています。
このコードでは、参考サイトのコードを拝借して 長いほうの辺を120pxとし縦横比を維持したまま伸縮するような計算にしていますが、 正直max()を使う必要があるのかどうかはよく分かりませんでした・・。

43~52行目
具体的にサムネイル化を行う処理をしています。
imagecreatefromjpeg()(ここでは$create())で元画像をサーバー上に用意し、 imagecreatetruecolor()でサムネイルの大きさの真っ黒の画像を作って imagecopyresampled()で元画像を黒い画像に貼り付けています。
たくさん引き数がありますが、この関数は 元の画像の指定した範囲を、貼り付けたい画像の指定した範囲に貼り付けてくれる関数です。
(意味がわからなくても、私の日本語力の問題なのでスルーしてください笑
ちなみにdstはdestination(行き先)、srcはsource(情報源)の略です。

imagecopyresampled ( $dst, $src, $dst_x, $dst_y, $src_x, $src_y, $dst_w,  $dst_h , $src_w, $src_h )

$dst:コピー先の画像リンクリソース
$src:コピー元の画像リンクリソース
$dst_x:コピー先の x 座標
$dst_y:コピー先の y 座標
$src_x:コピー元の x 座標
$src_y:コピー元の y 座標
$dst_w:コピー先の幅
$dst_h:コピー先の高さ
$src_w:コピー元の幅
$src_h:コピー元の高さ

言葉じゃうまく伝わらないので歌に・・、ではなく図にしてみました。

f:id:pen_00:20181029225144p:plain

座標は左上を原点に取り、srcの破線内をdstの破線内に貼り付けるということをしてくれます。

なのでサムネイルの作成でいうと、 「それぞれ画像の左上端である(0, 0)を原点に画像全体を選択するように指定して、貼り付けを行う」 という作業になります。

f:id:pen_00:20181029224615p:plain

余談ですが、「なぜ座標が左下原点じゃなくて左上原点なのか?」 という点についておそらくコードや横書きのテキストを読み込むのと同じように 上から下に一行ずつ、左から右へ1ピクセルずつ読み込んでいるんだと思います。(たぶん)

かなりだらだらと書きましたが、誤り等ありましたらご指摘いただけるとうれしいです。
ここまで読んでくださりありがとうございました!

参考URL

PHP+MySQLで簡易画像アップローダ - Qiita

PHP: imagecopyresampled - Manual

それでも僕が、エラー制御(抑制)演算子"@"を使う理由 - noopな日々

例外処理ってなんだろう

例外処理ってなあに。
 データベースへの画像アップロード→PHPで表示する方法について調べていたら、
RuntimeExceptionというものに遭遇してこれは何?と思ったので少し調べてみました。


例外処理って?

 ざっくり言うと、
エラーが起こるんじゃないかなって予想がつく処理には、実際にエラーが起きた時に
そのエラーが起きた原因を明確にできるようにあらかじめ準備しておく
ってことだと思います。
 ゴルフで言うと(なぜ)、意図せず人にボールをぶつけてしまった時のために
ゴルフ保険に入っておくみたいな?
私はドライバーフルスイングで50ヤードしか飛びませんが・・・笑
そんな感じで、エラーが起こった時にそのエラーについて
メッセージを表示させることだと理解しました。

一番簡単な型としては

<?php
    try{
        エラーが起こる可能性のある処理
    
    }
    catch(Exception $e){
        //エラーメッセージを表示させる
        exit($e->getMessage());

    }
?>

かな?


私がPDO接続の時に何も考えずおまじないとして使ってたこのtry-catchも

<?php
try {
    $dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);

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

PDOの接続時に起きたエラーに限定した例外処理を行っていたということだったのだ。
知らなかった!笑

PDOExceptionみたいに、Exceptionはいろんなクラスがあって
RuntimeExceptionは実行時にだけ発生するようなエラーの際にスローするものということでした。
だから、ユーザーがアップロードしようとした画像や動画に問題があった場合に
「サイズが大きすぎます」、「ファイル形式が対応していません」
といったメッセージを出すために使っているというわけか!


わからないこと

 調べた上でさらに疑問点が二つ。

  • tryの中でのスロー(throw new Exception();)は絶対じゃないの?
     いろんなコードを見ているとスローしてる場合とそうじゃない場合があるんです。
     なぜだろう。

  • Exceptionにいろいろ仲間(クラス)がいることはわかったけれど、
    なぜ使い分ける必要があるの?
     Exeption()で全部まかなってはいけないんでしょうか?



一重に例外処理といってもいろいろな書き方ができるんだなあということを学びました。

参考記事

PHPでの例外処理の方法:try...catch, throw | UX MILK

PHPの例外(Exception) | kudox.jp

PHP: RuntimeException - Manual

ブログにコードを載せる準備をしました。

みんなが使っているコードをいい感じに表示させるテーブルみたいなのって
どうやったら使えるようになるんだろう?いいなあ~
と思って調べてみたらはてなブログにはシンタックスハイライトというものがあるみたいですね。

色の付け方など好きなようにカスタマイズできるみたいなので、
追々そういうことも自力でできるようになりたいなー!

今回はこちらの記事のコードをお借りして、
行番号を表示できるように&行の色付けをしました。

am1tanaka.hatenablog.com

JavascriptCSSを使っているんですね。
こんなかんじです↓

  <?php
    echo "ペンまるだよ!";
  ?>

便利すぎるじゃないか・・・(感動
これだけでずいぶん技術ブログっぽくなりました。笑(形から入るタイプ
どんどん活用していきたいと思います~♪