[Laravel]Base64でエンコードされたデータをバリデーションする方法

[Laravel]Base64でエンコードされたデータをバリデーションする方法
2021年05月10日2023年10月10日

通常の画像送信であればFormtから「multipart/form-data」形式で送信してLaravelのFormRequestでバリデーション判定が可能ですが、ファイルがBase64の場合はチェックするバリデーションが用意されていません。

なぜBase64で画像を送信するのか?

スマホアプリとAPIの通信を暗号化して送受信したいという要望がありました。

通常のFormであれば、画像を「multipart/form-data」形式でPostすれば良いのですが、Formデータも暗号化するため「multipart/form-data」形式が使用できずテキストベースでのPost送信となりました。

その影響で、ファイルデータもテキストベース(Base64形式)にする必要がありました。

Base64を一時的にファイルに変換する

LaravelにはBase64のデータをバリデーションする機能がないため、一時的にファイルに変換してファイルとしてバリデーションを通す必要があります。

Base64だと実際のファイルサイズも異なりますし、正確なバリデーション処理ができません。

やり方はとても簡単です。FormRequestクラスにあるvalidationData()メソッドを使用します。

app/Http/Requests/TestRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\File\File;

class TestRequest extends FormRequest
{


    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'photo' => 'file|mimes:jpeg,png,jpg,gif|max:2000'
        ];
    }

    public function attributes()
    {
        return [
            "photo" => '写真'
        ];
    }

    protected function validationData()
    {
        $all = parent::validationData();

        // base64データをファイル形式詰めかる
        $all['photo'] = $this->base64ToFile($all['photo']);

        return $all;
    }

    /**
     * base64のデータを一時的にファイルに変換しファイルの添付情報を返却する関数
     *
     * @param string $base64
     * @return NULL|\Illuminate\Http\UploadedFile
     */
    private function base64ToFile(string $base64)
    {
        if (50 > strlen($base64)) {
            return null;
        }

        // preg_matchは文字数上限があるため前から50文字を取得
        $substrBase64 = substr($base64, 0, 50);

        if (! preg_match('/data\:.*\;base64\,/', $substrBase64)) {
            return null;
        }
        $explodeBase64 = explode(',', $base64);

        $content = base64_decode($explodeBase64[1]);

        // tempディレクトリを指定(処理後は自動的に削除される)
        $tmpFilePath = sys_get_temp_dir() . '/' . Str::uuid()->toString();

        // ファイルとして保存
        file_put_contents($tmpFilePath, $content);
        $tmpFile = new File($tmpFilePath);

        return new UploadedFile($tmpFile->getPathname(), $tmpFile->getFilename(), $tmpFile->getMimeType(), 0, true);
    }
}

Controller側は、下記のように取得できます。

app/Http/Controllers/TestController.php
namespace App\Http\Controllers;

use App\Http\Requests\TestRequest;

class TestController extends Controller
{

    public function store(TestRequest $request)
    {

        // 素のデータ(base64のままのデータ)
        $form = $request->all();

        // バリデーション済みデータの取得(base64がfileに変換されたデータ)
        $form = $request->validated();
    }
}

base64ToFileメソッド独自関数かしたほうが便利

base64をファイルに変換する関数は独自関数かしておくと便利だったりします。

独自関数化する方法は下記記事に参考にしてみてください。

Laravelはさまざまな、グローバル「ヘルパー」PHP関数を用意されています。どこからでも呼...

最後に

今回FormRequestクラスにあるvalidationData()メソッドを使ったやり方を紹介しましたが、composerでインストールできる「crazybooot/base64-validation」パッケージもあります。
(issuesを見ると不具合があるっぽい)

コメント

コメントを残す

お名前(任意)
コメント:新規