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

# ファイルサイズの上限
上限の設定を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バイト

```html

 <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されているか

```php
if (!isset($_FILES['image']) || !isset($_FILES['image']['error'])) {
  throw new \Exception('Upload Error!');
}
```

- エラーの内容によってエラーメッセージを出し分ける

```php
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関数を使う**

```php
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にするとさらに文字が長くなる。

- 格納ディレクトリ

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

```php
define('IMAGES_DIR', __DIR__ . '/images');
```

- 保存処理

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

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

# サムネイルを作る

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

```php
   $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で指定したディレクトリのファイル一覧にはテンプレがある。

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

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

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

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

```php
  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: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>
```

```javascript:jQuery
$(function() {
  $('.msg').fadeOut(3000);
  $('#my_file').on('change', function() {
    $('#my_form').submit();
  });
});
```

```css:css
input[type="text"]{
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}
```