laravel8 画像をBase64の文字列にエンコード・デコードするアプリを作る

# 目的

- laravelを用いて画像をBase64の文字列にエンコード・デコードする簡単なアプリを作ってみる

# 環境

- ハードウェア環境

| 項目 | 情報 |
| --- | --- |
| OS | macOS Big Sur(11.6) |
| ハードウェア | MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) |
| プロセッサ | 2 GHz クアッドコアIntel Core i5 |
| メモリ | 32 GB 3733 MHz LPDDR4 |
| グラフィックス | Intel Iris Plus Graphics 1536 MB |

- ソフトウェア環境

| 項目 | 情報 | 備考 |
| --- | --- | --- |
| PHP バージョン | 7.4.11 | Homebrewを用いてこちらの方法で導入→[Mac HomebrewでPHPをインストールする](https://qiita.com/miriwo/items/cd54077aad7e139cf518) |
| Laravel バージョン | 8.X | commposerを用いてこちらの方法で導入→[Mac Laravelの環境構築を行う](https://qiita.com/miriwo/items/adaaaeecae69d6392e08) |
| MySQLバージョン | 8.0.21 for osx10.13 on x86_64 | Homwbrewを用いてこちらの方法で導入→[Mac HomebrewでMySQLをインストールする](https://qiita.com/miriwo/items/ef6a064bc545fbea29d9) |

# ご注意

- Base64エンコードはデータ量が多くなるためサイズの大きい画像ファイルには不向きです。サイズの大きい画像ファイルはmultipart/form-data形式でのファイルのやり取りを検討しましょう！

# 概要

- Macに直接作成したlaravelのローカル開発環境にて今回のアプリを作成する。
- 下記機能を持つlaravel8のアプリを作ってみる。
    - 画像 → Base64文字列（エンコード）
    - Base64文字列 → 画像（デコード）
- 文字列のBase64エンコード・デコードは今回実装しない。
- 今回のソース
    - [https://github.com/miriwo0104/laravel8_base64](https://github.com/miriwo0104/laravel8_base64)

# 方法

1. 下記コマンドを実行してlaravel8のアプリを作成する。

    ```terminal
    $ composer create-project "laravel/laravel=8.*" laravel8_base64 -vvv
    ```

1. 上記コマンドをで作成された`laravel8_base64`ディレクトリに移動して下記コマンドを実行してローカルサーバーを起動する。

    ```terminal
    $ php artisan serve
    ```

1. 下記にアクセスし、正常にローカルサーバーが起動していることを確認する。
    - [http://127.0.0.1:8000/](http://127.0.0.1:8000/)
1. `laravel8_base64`ディレクトリで下記コマンドを実行してフォームリクエストクラスのファイルを作成する。

    ```terminal
    $ php artisan make:request EncodeRequest
    ```

1. `laravel8_base64/pp/Http/Requests/EncodeRequest.php`を開き下記の様に修正する。

    ```laravel8_base64/pp/Http/Requests/EncodeRequest.php 
    <?php
    
    namespace App\Http\Requests;
    
    use Illuminate\Foundation\Http\FormRequest;
    
    class EncodeRequest extends FormRequest
    {
        /**
         * Determine if the user is authorized to make this request.
         *
         * @return bool
         */
        public function authorize()
        {
            return true;
        }
    
        /**
         * Get the validation rules that apply to the request.
         *
         * @return array
         */
        public function rules()
        {
            return [
                'file' => 'required'
            ];
        }
    }
    ```

1. 下記コマンドを実行してフォームリクエストクラスのファイルを作成する。

    ```terminal
    $ php artisan make:request DecodeRequest
    ```

1. `laravel8_base64/pp/Http/Requests/DecodeRequest.php`を開き下記の様に修正する。

    ```laravel8_base64/pp/Http/Requests/DecodeRequest.php 
    <?php
    
    namespace App\Http\Requests;
    
    use Illuminate\Foundation\Http\FormRequest;
    
    class DecodeRequest extends FormRequest
    {
        /**
         * Determine if the user is authorized to make this request.
         *
         * @return bool
         */
        public function authorize()
        {
            return true;
        }
    
        /**
         * Get the validation rules that apply to the request.
         *
         * @return array
         */
        public function rules()
        {
            return [
                'base64_str' => 'required',
            ];
        }
    }
    ```
1. ルーティングファイルに下記の内容を記載する。

    ```laravel8_base64/routes/web.php
    <?php
    
    use App\Http\Controllers\ConvertController;
    use Illuminate\Support\Facades\Route;
    
    /*
    |--------------------------------------------------------------------------
    | Web Routes
    |--------------------------------------------------------------------------
    |
    | Here is where you can register web routes for your application. These
    | routes are loaded by the RouteServiceProvider within a group which
    | contains the "web" middleware group. Now create something great!
    |
    */
    
    Route::get('/', function () {
        return view('welcome');
    });
    
    Route::get('index', [ConvertController::class, 'index'])->name('index');
    Route::post('encode', [ConvertController::class, 'encode'])->name('encode');
    Route::post('decode', [ConvertController::class, 'decode'])->name('decode');
    ```

1. 下記コマンドを実行してコントローラークラスのファイルを作成する。

    ```terminal
    $ php artisan make:controller ConvertController
    ```

1. 作成された`laravel8_base64/app/Http/Controllers/ConvertController.php`を開き下記の様に記載する。

    ```laravel8_base64/app/Http/Controllers/ConvertController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use App\Http\Requests\EncodeRequest;
    use App\Http\Requests\DecodeRequest;
    use Illuminate\Support\Facades\Storage;
    use Illuminate\Http\Request;
    
    class ConvertController extends Controller
    {
        public function index()
        {
            return view('index');
        }
    
        public function encode(EncodeRequest $request)
        {
            $postData = $request->validated();
            // ファイルを文字列とする
            $file = file_get_contents($postData['file']);
            // ファイルをbase64でエンコード
            $encodedBase64Str = base64_encode($file);
            // view表示用データの整形
            $viewData = [
                'encodedBase64Str' => $encodedBase64Str,
            ];
        
            return view('index', ['viewData' => $viewData]);
        }
    
        public function decode(DecodeRequest $request)
        {
            $postData = $request->validated();
        
            // base64文字列のData-URL宣言の削除
            $base64Str = str_replace(' ', '+', preg_replace('/^data:image.*base64,/', '', $postData['decodeBase64Str']));
            // ファイルのデコード
            $decodeFile = base64_decode($base64Str);
            // mimeの取得
            $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $decodeFile);
        
            // view表示用データの整形
            $viewData = [
                'decodeBase64Str' => $base64Str,
                'mime' => $mime,
            ];
        
            return view('index', ['viewData' => $viewData]);
        }
    }
    ```

1. `index.blade.php`を作成して下記の内容を記載する。

    ```laravel8_base64/resources/views/index.blade.php
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Covert</title>
    </head>
    <body>
        <header></header>
        <main>
            <div class="encode">
                <h1>ファイル → Base64 へエンコード</h1>
                @error('file')
                    {{ $message }}
                @enderror
                @if ( empty($viewData['encodedBase64Str']) )
                    <form action="{{ route('encode') }}" method="post" enctype="multipart/form-data">
                        @csrf
                        <input type="file" name="file" id="input-file">
                        <input type="submit" value="変換">
                    </form>
                @else
                    <textarea name="" id="" cols="180" rows="10">{{ $viewData['encodedBase64Str'] }}</textarea>
                @endif
            </div>
            <div class="decode">
                <h1>Base64 → ファイル デコード</h1>
                @error('decodeBase64Str')
                    {{ $message }}
                @enderror
                @if ( empty($viewData['decodeBase64Str']) )
                    <form action="{{ route('decode') }}" method="post">
                        @csrf
                        <input type="text" name="decodeBase64Str" id="input-base64_str">
                        <input type="submit" value="変換">
                    </form>
                @else
                    <img src="data: {{ $viewData['mime'] }};base64, {{ $viewData['decodeBase64Str'] }}">
                @endif
            </div>
            <div class="clear">
                <a href="{{ route('index') }}">
                    <button>内容クリア</button>
                </a>
            </div>
        </main>
        <footer></footer>
    </body>
    </html>
    ```

1. 確認
    1. ローカルサーバーを起動して下記にアクセスする。
        1. [http://127.0.0.1:8000/decode](http://127.0.0.1:8000/decode)
    1. 「ファイルを選択」をクリックして何かしらの画像ファイルを選択し「変換」をクリックする。

        ![Covert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/30816cc2-b657-42a4-9def-9190583ab0df.png)

    1. Base64文字列が表示されるのですべてコピーする。

        ![Covert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/67281f30-9e3a-e746-5647-a10e2006d7fb.png)

    1. 「Base64 → ファイルデコード」のinput boxにコピーしたBase64文字列を入力して「変換」をクリックする。

        ![Covert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/931af03e-b46d-0f8d-d0cd-aea3e5b9360d.png)

    1. 画像に変換されて表示される。

        ![Covert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/8d29fc9a-cf73-126b-3212-1bee0c43913a.png)



# 参考文献

- [https://codelikes.com/php-base64-encode/](https://codelikes.com/php-base64-encode/)
- [https://qiita.com/taniko/items/c76c04576c8eb38191ad](https://qiita.com/taniko/items/c76c04576c8eb38191ad)
- [https://qiita.com/bashi4/items/611ac85056f735d1cd54](https://qiita.com/bashi4/items/611ac85056f735d1cd54)
- [https://qiita.com/mserizawa/items/7f1b9e5077fd3a9d336b](https://qiita.com/mserizawa/items/7f1b9e5077fd3a9d336b)