[PHP] フォームから投稿した画像の表示や削除処理

PHPで管理ツールなどをつくっていると、管理用に画像を扱いたいことがあります。

この場合、画像はサーバに保存し、画像のパスをDBに入れることが多いと思いますが、この画像の管理、ちょっと面倒だと思いませんか?

自分が普段使いまわしている画像関連処理を、自分用メモとしてまとめてみました。

アップロードされた画像の処理などは、共通関数化していますので、最後の方でまとめてソースを置いています。

※自分が操作する用の処理なので、不正対策やバリデーションチェックなどは考慮していません


スポンサーリンク

フォームから画像送信するための処理

フォームから画像などのファイルを送信する場合は、inputタグを使います。

<form method="post" action="<?php print basename( __FILE__ ); ?>" enctype="multipart/form-data">
  <input type="file" name="image" accept="image/png,image/jpeg">
  <input type="submit" value="送信">
</form>

この送信されたファイルを処理するため、PHPで以下の処理を追加します。

$fname = file_upload( $_FILES, 'image' );

file_uploadは自作共通関数です。後述しているファイルアップロード処理関数を参照してください。この関数から戻されたファイル名($fname)を、DBに保存します。

一連の処理をまとめると、以下のような感じ。

<?php
try {
    $fname = file_upload( $_FILES, 'image' );
    # DBに登録する処理
} catch( Exception $e ){
    # エラー処理
}
# DBから読み込み処理
?>
<form method="post" action="<?php print basename( __FILE__ ); ?>" enctype="multipart/form-data">
  <input type="file" name="image" accept="image/png,image/jpeg">
  <input type="submit" value="送信">
</form>

送信済みファイルを削除したい場合

画像ファイルが既に設定されていて、その画像ファイルを削除したい場合は、削除用のフラグを追加します。

上記処理で、imageに値がセットされていたら、削除用のチェックボックスを表示するように変更。

<?php
$image_del = filter_input( INPUT_POST, 'image_del', FILTER_SANITIZE_STRING );
try {
    if( empty( $image_del ) || $image_del !== '1' ){
        $fname = file_upload( $_FILES, 'image' );
        # DBに画像パスを登録するSQLを作成
    } else {
        # DBから画像パスを削除するSQLを作成
    }
    # DBの更新処理
} catch( Exception $e ){
    # エラー処理
}
?>
<form method="post" action="<?php print basename( __FILE__ ); ?>" enctype="multipart/form-data">
  <input type="file" name="image" accept="image/png,image/jpeg">
<?php if( strlen( $fname ) > 0 ){ ?>
  <input type="checkbox" name="image_del" value="1">登録済画像を削除する</input>
<?php } ?>
  <input type="submit" value="送信">
</form>

ちなみに、type="file"では、valueに画像ファイルを指定しても、指定した値は反映されません。JavaScriptを使っても設定できません。

これは、ユーザーが意図しないファイルを、勝手にサーバ側で指定して送信してしまう、という状況を防ぐためのものなので、セキュリティ上不可能です。


送信済みファイルがある場合にサムネイルを表示

画像ファイルが既に設定されていて、その画像ファイルをサムネイル表示したい場合は、imgタグを追加します。

上記処理にさらに追加します。

<?php
$image_del = filter_input( INPUT_POST, 'image_del', FILTER_SANITIZE_STRING );
try {
    if( empty( $image_del ) || $image_del !== '1' ){
        $fname = file_upload( $_FILES, 'image' );
        # DBに画像パスを登録するSQLを作成
    } else {
        # DBから画像パスを削除するSQLを作成
    }
    # DBの更新処理
} catch( Exception $e ){
    # エラー処理
}
?>
<form method="post" action="<?php print basename( __FILE__ ); ?>" enctype="multipart/form-data">
  <input type="file" name="image" accept="image/png,image/jpeg">
<?php if( strlen( $fname ) > 0 ){ ?>
  <input type="checkbox" name="image_del" value="1">登録済画像を削除する</input>
  <img src="<?php print $fname; ?>" style="width: 100px; height: auto;">
<?php } ?>
  <input type="submit" value="送信">
</form>

ファイルアップロード処理関数

上記処理で使用しているファイルアップロード処理関数です。

ひとつめのパラメータは、$_FILESをそのまま渡してください。
ふたつめのパラメータは、POSTされてくるname属性の値を渡してください。

戻り値として、ファイルへのパスが設定されます。

function file_upload( $argFile, $argColum ){
    // ファイル情報が取得できていない場合は処理を抜ける
    if( !isset( $argFile[$argColum] ) ) return '';

    $finfo = $argFile[$argColum];

    // ファイルが送信されていない場合は処理を抜ける
    if( empty( $finfo['tmp_name'] ) ) return '';

    // ファイルアップロード時のエラーチェック
    if( strlen( $finfo['tmp_name'] ) > 0 && $finfo['error'] !== UPLOAD_ERR_OK ){
        $reason = '';
        switch( $finfo['error'] ){
            case UPLOAD_ERR_INI_SIZE:
                 $reason = "php.iniのupload_max_filesizeディレクティブの値を超えています";
                 break;
            case UPLOAD_ERR_FORM_SIZE:
                 $reason = "HTMLフォームで指定されたMAX_FILE_SIZEを超えています";
                 break;
            case UPLOAD_ERR_PARTIAL:
                 $reason = "一部のみしかアップロードされていません";
                 break;
            case UPLOAD_ERR_NO_FILE:
                 $reason = "ファイルはアップロードされませんでした";
                 break;
            case UPLOAD_ERR_NO_TMP_DIR:
                 $reason = "テンポラリフォルダがありません";
                 break;
            case UPLOAD_ERR_CANT_WRITE:
                 $reason = "ディスクへの書き込みに失敗しました";
                 break;
            case UPLOAD_ERR_EXTENSION:
                 $reason = "PHPの拡張モジュールがファイルのアップロードを中止しました";
                 break;
        }
        throw new Exception( $reason );
    }

    $tmpname = $finfo['tmp_name'];

    // 指定されたファイルがアップロードされたものかチェック
    if( !is_uploaded_file( $tmpname ) ){
        $reason = '指定されたファイルはPOSTでアップロードされたファイルではありません';
        throw new Exception( $reason );
    }

    // アップロードされたファイルの画像形式チェック
    $ext = '';
    if( @exif_imagetype( $tmpname ) === IMAGETYPE_JPEG ){
        $ext = '.jpg';
    }
    else if( @exif_imagetype( $tmpname ) === IMAGETYPE_PNG  ){
        $ext = '.png';
    }
    else {
        $reason = '画像形式が未対応です';
        throw new Exception( $reason );
    }

    // アップロードされた画像の移動処理
    $fname = './images/'.date( 'YmdHis ).$ext;
    if( !move_uploaded_file( $tmpname, $fname ) ){
        $reason = 'ファイル保存時にエラーが発生しました';
        throw new Exception( $reason );
    }

    return $fname;
}

さいごに

だいぶ簡略化しているので、実際に使っていくには処理部分を作り込んだり、レイアウトを整える必要があります。以下はレイアウト例。

レイアウト例

自分だけが使うシステムになるので、利便性などは特に考慮していません。

file_upload関数に関しては、実際に使っているものは少し違います。実際は、処理ごとにデバッグ出力用の処理があったり、例外処理ももうちょっと作り込んでいます。でも、ほぼこのままで使い回せるんじゃないかな。

ご参考までにどうぞ。


初稿:2019年2月6日

スポンサーリンク

この記事をシェア

アカウントをフォロー