【Laravel】getMimeTypeとgetClientMimeTypeの違いと取得できる値の比較


# はじめに

　この記事では、getMimeTypeとgetClientMimeTypeのそれぞれの定義と取得できる値の違いをまとめました。

「あれ、このファイルをアップロードした時にどんな値がとれるんだっけ？」

って時にさっと確認できるように比較表を記載してますので、結果だけ知りたい場合は[結果](https://qiita.com/sousyunesk/items/e7a2d0dddaab8e630fc6#%E7%B5%90%E6%9E%9C)の比較表をご覧ください。


## 背景

　Laravelを利用したWEBアプリケーションを開発中に、ユーザーがアップロードした画像に対してMIMEタイプ別に加工処理をする必要が出てきたのがきっかけです。

調べたところ、getMimeTypeとgetClientMimeTypeの２つがあり、どちらを使うのが正しいのか、それぞれ取得できる値に違いがあるのか気になったので、まとめてみやすいようにしてみました。


　~~また個人的な理由ですが、いままで「仕事が忙しい」を言い訳にいっさいアウトプットをしてこなかったため、今年こそはと一念発起して書き始めました。備忘録兼自分の理解を深める目的で書いてますので、内容に間違いがあったり、もっといいやり方や別のやり方等がありましたら、コメントいただけると嬉しいです！~~


## 環境
（Docker上でPHPの実行環境を作って試しています。）
Debian GNU/Linux: 10.6
PHP: 7.4.12
Laravel Framework: 8.13.0

# getMimeTypeとgetClientMimeTypeについて

## 定義
### getMimeType()

```php:route_folder/vendor/symfony/http-foundation/Request.php

<?php

namespace Symfony\Component\HttpFoundation;

use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

/* 〜〜〜〜省略〜〜〜〜〜 */

class Request
{
    /* 〜〜〜〜省略〜〜〜〜〜 */

    /**
     * Gets the mime type associated with the format.
     *
     * @return string|null The associated mime type (null if not found)
     */
    public function getMimeType(string $format)
    {
        if (null === static::$formats) {
            static::initializeFormats();
        }

        return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
    }

    /* 〜〜〜〜省略〜〜〜〜〜 */
}
```

### getClientMimeType()
```php:route_folder/vendor/symfony/http-foundation/File/UploadedFile.php
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\HttpFoundation\File;
    /* 〜〜〜〜省略〜〜〜〜〜 */

/**
 * A file uploaded through a form.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Florian Eckerstorfer <florian@eckerstorfer.org>
 * @author Fabien Potencier <fabien@symfony.com>
 */
class UploadedFile extends File
{
    /* 〜〜〜〜省略〜〜〜〜〜 */

    /**
     * Returns the file mime type.
     *
     * The client mime type is extracted from the request from which the file
     * was uploaded, so it should not be considered as a safe value.
     *
     * For a trusted mime type, use getMimeType() instead (which guesses the mime
     * type based on the file content).
     *
     * @return string The mime type
     *
     * @see getMimeType()
     */
    public function getClientMimeType()
    {
        return $this->mimeType;
    }

    /* 〜〜〜〜省略〜〜〜〜〜 */
}
```

## 違いと使い分け
getClientMimeType()のコメント部分にがっつり記載されてました。

> Returns the file mime type.
>
> The client mime type is extracted from the request from which the file
> was uploaded, so it should not be considered as a safe value.
>
> For a trusted mime type, use getMimeType() instead (which guesses the mime
> type based on the file content).

`getClientMimeType()`はファイルをアップロードしたリクエストから抽出されたものなので正確なものではなく、信頼できるMIMEタイプを取得したい場合は`getMimeType()`を使ってください。とのことでした。

またこちらにも同じような内容の質問で、とても分かりやすい回答が載ってました。
https://stackoverflow.com/questions/39594854/why-does-laravels-getmimetype-method-identify-a-file-as-application-octet-st

こちらのサイトの答えをお借りすると、

`getMimeType()`　->　ファイルの内容をみてMIMEタイプを判断

`getClientMimeType()`　->　ブラウザが判定した情報を元にMIMEタイプを判断


なので、正確にMIMEタイプを撮りたい場合は`getMimeType()`を使うと良さそうでした。

ただ、上記の質問者の方のようにmp3をあげたはずなのに`getMimeType()`だと別の値になってしまうとのことで、取得できる値を整理してみました。


# 準備
今回は実際に取得できる値を確認するだけなので、DBへの保存等はせず、ログに吐き出すだけの処理を実装しました。

```php:route_folder/routes/web.php
Route::get('/file/upload', 'LectureController@index');
Route::post('/file/upload', 'LectureController@upload');
```

```php:route_folder/app/Http/Controllers/FileUploadController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class FileUploadController extends Controller
{

    public function index()
    {
        return view('file.upload');
    }

    public function upload(Request $request)
    {
        $images = $request->file('images');
        foreach ($images as $image) {
            logger('拡張子: '.$image->getClientOriginalExtension());
            logger('getMimeType(): '.$image->getMimeType());
            logger('getClientMimeType(): '.$image->getClientMimeType());
        }

        return redirect('/file/upload');
    }


}
```

```php5:route_folder/resources/views/file/upload.blade.php

<div>
    <form action="/file/upload" method="POST" enctype='multipart/form-data'>
        @csrf
        <h2>画像アップロード</h2>
        <div>
            <input type="file" name="images[]" multiple="multiple">
        </div>
        <button type="submit">更新</button>
    </form>
</div>

```

出力結果

```text:route_folder/storage/logs/laravel-2021-02-23.log
[2021-02-23 12:58:26] stack-channel.DEBUG: 拡張子: pdf  
[2021-02-23 12:58:26] stack-channel.DEBUG: getMimeType(): application/pdf  
[2021-02-23 12:58:26] stack-channel.DEBUG: getClientMimeType(): application/pdf  
```

# 結果
基本的にはどちらも取得結果は同じでしたが、

画像系のファイルだと`svg`,

テキスト系のファイルだと`csv,txt,log,nfo,srt`、

動画系のファイルだと`wav, aiff`

辺りが違ってたので、テキストファイルや動画ファイルあたりを扱うときにはをつけた方が良さそうでした。


| 拡張子 | getMimeType() | getClientMimeType() |
|:-:|:-:|:-:|
| .jpeg,jpg,jpe,jfif | image/jpeg | image/jpeg |
| .png | image/png | image/png |
| .gif | image/gif | image/gif |
| .HEIC | image/heic | image/heic |
| .ai | application/postscript | application/postscript |
| .psd | image/vnd.adobe.photoshop | image/vnd.adobe.photoshop |
| .tif | image/tiff | image/tiff |
| .svg | text/html | image/svg+xml |
| .csv | text/plain | text/csv |
| .txt | text/plain | text/plain |
| .log | text/plain | application/octet-stream |
| .nfo | text/plain | application/octet-stream |
| .srt | text/plain | application/octet-stream |
| .xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| .doc | application/msword | application/msword |
| .docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document | application/vnd.openxmlformats-officedocument.wordprocessingml.document  |
| .pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation  | application/vnd.openxmlformats-officedocument.presentationml.presentation |
| .mp3 | audio/mpeg | audio/mpeg |
| .mid | audio/midi | audio/midi |
| .wav | audio/x-wav | audio/wav |
| .aiff | audio/x-aiff | audio/aiff |
| .ogg | audio/ogg | audio/ogg |
| .mov | video/quicktime | video/quicktime |
| .zip | application/zip | application/zip |
| .rar | application/x-rar | application/x-rar |


# 引用
https://stackoverflow.com/questions/39594854/why-does-laravels-getmimetype-method-identify-a-file-as-application-octet-st

https://qiita.com/mashirou_yuguchi/items/14d3614173c114c30f02

http://www.tohoho-web.com/ex/draft/extension.htm