Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What is going on with this article?
@naoqoo2

【Laravel】Base64エンコードされた画像データをファイルに変換してバリデーションする

はじめに

画像アップロードAPIを実装する際、webだと「multipart/form-data形式」なことが多いですが、アプリは「Base64形式」で処理したいといったことがあります(ありました)

Base64をバリデーションしたい

Laravelはバリデーションが豊富なので、Base64もいい感じに処理してくれるかなーと思ったのですが、そんなに甘くはありませんでした。

ググったら、Base64用のバリデーション書け的な感じで、そうなるとアップロード処理もbase64用に書く必要がありそうです。
すでに画像ファイルのアップロード処理は実装済だったので、バリデーション前にBase64を画像ファイルに変換してあげることができればよさそうです。

Base64をファイルに変換して処理する

「multipart/form-data」で送信したファイルをFormRequestでdd()してみると、UploadedFileオブジェクトであることがわかりました。
FormRequestでバリデーション前に何か処理をするにはvalidationData()に記述します。ここでBase64をUploadedFileに変換してあげます。

せっかくなのでパラメータを2つ用意し、どちらの形式でもアップロードできるようにしました。

  • imageFile:multipart/form-data形式
  • imageBase64:Base64形式

<?php

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

class UploadImageRequest extends FormRequest
{
    public function validationData()
    {
        $all = parent::validationData();

        // imageBase64パラメータがあればUploadedFileオブジェクトに変換してimageFileパラメータに上書きする。
        if ($this->has('imageBase64')) {
            // base64をデコード。プレフィックスに「data:image/jpeg;base64,」のような文字列がついている場合は除去して処理する。
            $data = explode(',', $this->get('imageBase64'));
            if (isset($data[1])) {
                $fileData = base64_decode($data[1]);
            } else {
                $fileData = base64_decode($data[0]);
            }

            // tmp領域に画像ファイルとして保存してUploadedFileとして扱う
            $tmpFilePath = sys_get_temp_dir() . '/' . Str::uuid()->toString();
            file_put_contents($tmpFilePath, $fileData);
            $tmpFile = new File($tmpFilePath);
            $file = new UploadedFile(
                $tmpFile->getPathname(),
                $tmpFile->getFilename(),
                $tmpFile->getMimeType(),
                0,
                true // Mark it as test, since the file isn't from real HTTP POST.
            );
            $all['imageFile'] = $file;
        }

        return $all;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'imageFile' => 'nullable|image|mimes:jpeg|max:5000|dimensions:max_width=1200,max_height=1200,ratio=1/1', // ファイルのバリデーションよしなに。
            'imageBase64' => 'nullable|string', // 画像データをbase64で文字列としても受け入れる。バリデーションルールはimageFileが適用される。
        ];
    }
}

あとは$request->validated()['imageFile']を使ってアップロード処理を実装するだけです。
共通化できて最&高 DJ KOO イージードゥーダーンス!!

さいごに

テスト用にファイルをbase64に変換して送信するフォームを作ったので載せておきます。

<html>
<head>
</head>
<body>
    <form action="http://localhost/uploadImage" method="post" enctype="multipart/form-data">
        <p>アップロードするファイルを選択して下さい。</p>
        <p><input type="file" name="imageFile"></p>
        <input type="submit" value="保存">

        <p>base64で送る用<input id="file" type="file"></p>
        <div id="result"></div>
    </form>
<script>
    var file = document.getElementById('file');
    var result = document.getElementById('result');

    function loadLocalImage(e) {
        // ファイル情報を取得
        var fileData = e.target.files[0];

        // 画像ファイル以外は処理を止める
        if (!fileData.type.match('image.*')) {
            alert('画像を選択してください');
            return;
        }

        // FileReaderオブジェクトを使ってファイル読み込み
        var reader = new FileReader();
        // ファイル読み込みに成功したときの処理
        reader.onload = function () {
            // ブラウザ上に画像を表示する
            var img = document.createElement('img');
            var base64_string = reader.result;
            img.src = base64_string;
            result.appendChild(img);
            var input_hidden = document.createElement('input');
            input_hidden.type = 'hidden';
            input_hidden.name = 'imageBase64';
            input_hidden.value = base64_string;
            // input_hidden.value = base64_string.replace('data:image/jpeg;base64,', ''); // こっちでもいける。
            result.appendChild(input_hidden);
        }
        // ファイル読み込みを実行
        reader.readAsDataURL(fileData);
    }

    // ファイルが指定された時にloadLocalImage()を実行
    file.addEventListener('change', loadLocalImage, false);
</script>
</body>
</html>

参考

8
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
naoqoo2
myhm
住宅に関わるすべての人にオドロキとヨロコビを

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
8
Help us understand the problem. What is going on with this article?