質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.12%

phpでアップロードするファイルの拡張子を制限したい。

解決済

回答 2

投稿

  • 評価
  • クリップ 3
  • VIEW 9,020

fearless0603

score 39

phpで以下の順序でバリデーションを実施したいです。
1.ファイルが選択されていること
2.拡張子がpdf,jpg,pngのいずれかであること
3.ファイルサイズが2M以下であること

◆補足
・1~3のいずれかのチェックでエラーと判定された場合は後続のチェックを打ち切る
・エラーが見つかった場合はクラス内(グローバル)変数にエラーメッセージを設定する
・戻り値はboolean
→true:エラーなし、false:エラーあり

コード
                        <form action="settle_selectable_detail.php" method="post" enctype="multipart/form-data">
                                <input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
                                      <!--1024*1024*2 = 2MB,ユーザー側ブラウザにファイルのアップロードの最大サイズを指定する。-->
                                    <input type="file" name="upfile" size="30" />
                                    <input type="submit" value="アップロード">
                        </form>
コード
<?php

        if (is_uploaded_file($_FILES["upfile"]["tmp_name"])) {
          if (move_uploaded_file($_FILES["upfile"]["tmp_name"], "files/" . $_FILES["upfile"]["name"])) {
            chmod("files/" . $_FILES["upfile"]["name"], 0644);
            echo $_FILES["upfile"]["name"] . "をアップロードしました。";
          } else {
            echo "ファイルをアップロードできません。";
          }
        } else {
          echo "ファイルが選択されていません。またはサイズが2MBを超えています";
        }
         ?>

アドバイスお願いします・・・・

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

自分は管理画面などのページでよく使い回すので拡張子のチェックを関数にしています。

<?
    function chk_ext( $chk_name, $allow_exts=array( "png", "pdf", "jpg" ) ) {
        //使用出来ない拡張子のチェック
        $ext_err = true;//エラーフラグは初期値 真
        $exts = preg_split( "/[.]/", $chk_name );// ファイル名を.で分割する。
        if( count( $exts ) < 2 ) return false;
        $ext = $exts[ count( $exts ) - 1 ];//.で分割した最後のブロックの文字列を取得する
        foreach(  $allow_exts as $val ) {
            if( !empty( $val ) ) {
                //$len = strlen( $val );
                //if( strncasecmp( strtoupper($val), strtoupper($ext), $len ) == 0 ) {
                if( strcasecmp( $val, $ext ) == 0 ) { //修正しました(2016/03/23/01:22)
                    $ext_err = false;//エラーフラグ 偽に変更
                    break;
                }
            }
        }
        $ret = !$ext_err;//戻り値はエラーフラグを反転
        return $ret;
    }
    //ファイル名の拡張子チェックテスト
    $err_text = "";
    if( !chk_ext( $_FILES["upfile"]["name"] ) ) {
        $err_text .= "認められていない拡張子です。";
    }
?>


その他にファイルのアップロード時に検証した方が良い項目は以下のようなものがあります。

A. 日本語がファイル名に含まれていないか?
B. ファイル名にA-Za-z_-. 以外の文字が含まれていないか?
C. POST時にエラーが発生していないか?
D. ファイル名が空でないか?
E. 不正なパスを表す文字列がファイル名に含まれていないか?( /  や ../ など )
F. ファイルサイズが0バイトより大きく指定値以内か?
G. 本当にアップロードされたファイルなのか?(is_uploaded_file()で確認します)
H. 既存ファイルが存在していないか?(削除して上書きする、またはエラーとするか?)

・A,B,Eについて
 1. POST時にファイル名に指定の英数字と記号以外の文字が含まれていないか及び、
    拡張子が存在しているかのチェックをjavascriptで行います。(拡張子のチェックは手抜きでPHPのみです)

<script type="text/javascript">
<!--
function selOk( ) {
    var str = document.form1.upfile.value;
    if( str.match(/[^A-Za-z0-9_\-\.]+/)  ) {
        alert("ファイル名に英数字._-以外の文字が含まれています。");
        return false;
    }
    var ext_i = str.lastIndexOf( ".", str );
    if( ext_i <= 0 ) {
        alert("ファイル名に拡張子がありません");
        return false;
    }
    return true;
}
-->
</script>


submitボタンをクリックした時に呼び出してチェックします。

<input name="SubmitFile" type="submit" value="アップロード"  onClick="return selOk()">


 2. POST後にPHPで同様にチェックします。

<?
    function is_ansi( $str ) {
        //英数字._-だけかチェックします
        if( strlen( $str ) <= 0 ) return false;
        if( preg_match( "/[^A-Za-z0-9_\.\-]/", $str ) ) {
            return false;
        } else {
            return true;
        }
    }
    //ファイル名のチェック
    $userfile = basename( $_FILES['upfile']['name'] );
    if( !is_ansi( $userfile ) ) {
        $err_text .= "ファイル名に英数字._-以外の文字列が含まれています。"; 
    }
?>


・Cについて
エラー情報の $_FILES['upfile']['error']の値が0より大きい場合はエラーになります。

・Fについて
$_FILES['upfile']['size']をチェックします。
サーバーのphp.iniの設定が2Mバイトになっているかも確認してみてください。

<?
    $MAXSIZE = 2097152;
    if( ( $_FILES['upfile']['size'] > 0 )  && ( $_FILES['upfile']['size'] < $MAXSIZE ) ) {
        //サイズがOKな場合の処理
?>


move_uploaded_file()のコピー先の指定は安全の為にフルパスで記述するほうが良いかと思います。
纏めるとこんな感じになります。

<?
    $MAXSIZE = 2097152;
    $save_dir = "/home/sample-net/public_html/images/";
    $info = "";
    $err_text = "";
    if( !empty( $_FILES['upfile']['name'] ) && empty( $_FILES['upfile']['error'] ) ) {
        if( ( $_FILES['upfile']['size'] > 0 )  && ( $_FILES['upfile']['size'] < $MAXSIZE ) ) {
            if( is_uploaded_file( $_FILES['upfile']['tmp_name'] ) ) {
                //元のファイル名を取得
                $userfile = basename( $_FILES['upfile']['name'] );
                if( chk_ext( $userfile ) && is_ansi( $userfile ) ) {
                    //保存先ファイル名のフルパスを作成
                    $dest_name = $save_dir . $userfile;
                    $tmpfile = $_FILES['upfile']['tmp_name'];
                    if( !file_exists( $dest_name ) ) {
                        //ファイルのコピー処理
                        if( !move_uploaded_file( $tmpfile, $dest_name ) ) {
                            $err_txt .= "ファイルコピーに失敗しました $userfile...$tmpfile > $dest_name<br>\n";
                        } else {
                            $info .= "アップロードに成功しました $userfile ... $dest_name<br>\n";
                            chmod( $dest_name, 0644 );
                        }
                    } else {
                        $err_text .= "既存のファイルです。<br>\n";
                    }
                } else {
                    $err_text .= "ファイル拡張子が認められない形式かファイル名に半角英数字._-以外が含まれています。<br>\n";
                }
            } else {
                $err_text .= "ファイルがアップロードされていません。<br>\n";
            }
        } else {
            $err_text .= "ファイルサイズが指定サイズ外です。<br>\n";
        }
    } else {
        $err_text .= "ファイルアップロードエラーです。<br>\n";
    }
?>

//formタグの直前あたりにテキストを表示します
<?
    if( !empty( $info ) )print $info;
    if( !empty( $err_text ) )print $err_text;
?>


・Hについて
file_exists( )関数で既存ファイルが無いかチェックし、上書きするならunlink()関数を呼びます。

<?
    if( file_exists( $dest_name ) ) {
        if( unlink( $dest_name ) ) {
            $info .= "既存の同名ファイルを削除しました。<br>\n";
        }
    }
?>


(これだけ書くとエディタのプレビューが重くなるのは困ったものです。)
動作確認環境 Windows8.1 Pro Firefox 45.0.1

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

完璧ではないけどこんな感じかな?
細かい変数名とかは自分で修正お願いします。。。

<?php 
if (is_uploaded_file($_FILES["upfile"]["tmp_name"])) {
  // ファイルが選択されている場合拡張子チェック
  $fileType = pathinfo($_FILES["upfile"]["name"], PATHINFO_EXTENSION);
  if ($fileType == 'pdf' || $fileType == 'jpg' || $fileType == 'png') {
    // 拡張子がpdfまたはjpgまたはpngの場合ファイルサイズチェック
    if ($_FILES['upfile']['size'] < 2097152) {
      echo 'ok!';
    } else {
      echo 'ファイルサイズが大きすぎます!';
    }
  } else {
    // pdf jpg png 以外の場合
    echo "拡張子はpdf,jpg,pngです!!!";
  }
} else {
  // ファイルが選択されていない場合
  echo "ファイルが選択されていません。";
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる