LoginSignup
0
0

More than 3 years have passed since last update.

画像アップロードの留意点

Last updated at Posted at 2020-03-17

ファイルサイズの上限

上限の設定をPHP側でするのかブラウザ側でするのかで作業内容が変わる。
PHP側で設定してもブラウザ側で何も設定していない場合、上限のチェックが入るのはサーバにファイルをアップロード処理をしてからとなってしまう。
よって、巨大なファイルを転送しようとして長時間待ち続け、 結局ファイルが大きすぎてアップロードできなかったという事態に陥る。

  • PHP側で設定する場合

php.iniのupload_max_filesizeに設定する。
upload_max_filesizeは1ファイルあたりの上限サイズの設定
post_max_sizeはPOST全体のサイズ上限
post_max_sizeはupload_max_filesizeよりも常に大きくなければならない。

  • ブラウザ側で設定する場合

"file"inputフィールドの前に"MAX_FILE_SIZE"hiddenフィールドを置く必要がある。
この値はバイト数で指定する。

例).1MBの場合は1024*1024= 1048576バイト


 <input type="hidden" name="MAX_FILE_SIZE" value="1048576">
    <input type="file" name="image">

ファイルをアップしようとして、MAX_FILE_SIZE の制限を超えている場合、
$_FILESの['type']['tmp_name']は空である。

参考記事
http://hensa40.cutegirl.jp/archives/930

ファイルの検証

  • そもそもファイルがちゃんとPOSTされているか
if (!isset($_FILES['image']) || !isset($_FILES['image']['error'])) {
  throw new \Exception('Upload Error!');
}
  • エラーの内容によってエラーメッセージを出し分ける
switch($_FILES['image']['error']) {
  case UPLOAD_ERR_OK:
   return true;
  case UPLOAD_ERR_INI_SIZE: //php.iniのupload_max_filesize超過
  case UPLOAD_ERR_FORM_SIZE: //HTMLフォームで指定されたMAX_FILE_SIZE超過
   throw new \Exception('File too large!');
  default:
   throw new \Exception('Err: ' . $_FILES['image']['error']);
 }

参考記事
https://www.softel.co.jp/blogs/tech/archives/1824

画像ファイルの判別

exif_imagetype関数を使う

switch(exif_imagetype($_FILES['image']['tmp_name'])) {
      case IMAGETYPE_GIF:
        return 'gif';
      case IMAGETYPE_JPEG:
        return 'jpg';
      case IMAGETYPE_PNG:
        return 'png';
      default:
        throw new \Exception('PNG/JPEG/GIF only!');
    }

実際にアップロードする

  • ファイル名を決める

ファイル名は被らないように考慮する必要があるためuniqid関数を使うとよい。
これはマイクロ秒単位の現在時刻に基づいた13文字のIDが生成される。第二引数をtrueにするとさらに文字が長くなる。

  • 格納ディレクトリ

予め定数で格納ディレクトリを決めておくとよい。

define('IMAGES_DIR', __DIR__ . '/images');
  • 保存処理

move_uploaded_file関数を使う。引数にはファイル名を含むパスを指定する。
処理が失敗した場合はFALSEが返ってくるため、その場合は例外を投げるようにする。

    $savePath = IMAGES_DIR . '/' . $this->_imageFileName;
    $res = move_uploaded_file($_FILES['image']['tmp_name'], $savePath);
    if ($res === false) {
      throw new \Exception('Could not upload!');
    }

サムネイルを作る

横幅が大きい場合、サムネイルを作るためまずは画像サイズのチェックをする

   $imageSize = getimagesize($savePath);
    $width = $imageSize[0];
    $height = $imageSize[1];
    if ($width > THUMBNAIL_WIDTH) {
      $this->_createThumbnailMain($savePath, $width, $height);
      }
    }

サムネイルを作るには元画像の画像リソースを作り、それを元にサムネイルを作ってそれで保存をする。
(※gif,jpg,pngで、元画像を読み込むときと、サムネイルに加工後に出力する時の2つのポイントで関数が異なる。)

全体的な流れ

  • 元画像のリソースを読み込む
    • (imagecreatefromgif,imagecreatefromjpeg,imagecreatefrompng)
  • サムネイルの型を作る
    • (imagecreatetruecolor関数)
  • 実際にサムネイルを作る
    • (imagecopyresampled関数)
  • サムネイル画像を出力する
    • (imagegif,imagejpeg,imagepng)

関連記事
https://webkaru.net/php/image-thumbnail/
https://the-zombis.sakura.ne.jp/wp/blog/2012/01/03/post-1154/
https://00m.in/UBZ2n

画像一覧を表示する

基本的な流れとしては画像を取得してループ。
PHPで指定したディレクトリのファイル一覧にはテンプレがある。

    $imageDir = opendir(IMAGES_DIR);
    while (false !== ($file = readdir($imageDir))) {
      if ($file === '.' || $file === '..') {
        continue;
      }
      $files[] = $file;

アップロード成功時・失敗時のメッセージを出力する

メッセージを出力する関数には成功時・失敗時両方のメッセージを返すようにする

  public function getResults() {
    $success = null;
    $error = null;
    ...処理
    return [$success, $error];
  }

一覧画面では両方を一度に受け取る(list関数)

  list($success, $error) = $uploader->getResults();

アップロードボタンのスタイリング

画像ファイルを選択したらそのままアップロードされるようにする。
"submit"フィールドを消して、fileフィールドを透明にしつつボタン横いっぱいまで広げ、jQueryで要素が変わったらsubmitボタンが有効になるようにする

イベントの詳細
https://qiita.com/bibouroku/items/6b998ab90194b473286e#%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E9%96%A2%E9%80%A3%E3%81%AE%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88

HTML
<div class="btn">
  アップロードする
  <form action="" method="post" enctype="multipart/form-data" id="my_form">
    <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo h(MAX_FILE_SIZE); ?>">
    <input type="file" name="image" id="my_file">
  </form>
</div>
jQuery
$(function() {
  $('.msg').fadeOut(3000);
  $('#my_file').on('change', function() {
    $('#my_form').submit();
  });
});
css
input[type="text"]{
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0