Laravelで画像をwebpに対応させて軽量化する

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、皆さんは「Webp」(うぇっぴー)という画像形式を聞いたことがあるでしょうか。

Webpとは、グーグルが作った軽量な画像形式のことで、

JPEGと比べると平均30%もファイルサイズが減る

と言われています!

ただ、このWebp、実は2010年にリリースましたが、私も含めてあまり採用している人は見かけませんでした。

なぜなら、

当時はGoogle Chromeだけしか表示できなかったから

です。

そうです。IEは当然として(ゴメンナサイ)、FirefoxSafariでもサポートされていなかったので、「軽いのはわかるけど、表示できないブラウザがあったら、使えないでしょ…💧」となっていました。

しかし、その後状況は一変します。

2019年にFirefoxでサポートされると、2020年にはSafariでも表示が可能になり、さらにその傍らでIEがほぼ消滅&EdgeChromiumベースになったことで、2020年12月現在ではなんと主要なブラウザはほぼ全てWebpをサポートしているようになりました。

📝 Webpのサポート状況の確認はこちら(Can I use)

そこで❗

今回は、Laravelからアップロードされた画像をWebpに変換し、Vueコンポーネントで表示するところまで実装してみたいと思います。(なお、表示できない古いブラウザにも対応できるようにします👍)

ぜひ開発の参考になりましたら嬉しいです😊✨
(最後に今回開発したソースコード一式をダウンロードできますよ👍)

「着るコタツ、最高に使えるアイテムでした😊✨」

開発環境: Laravel 8.x、Vue 3、axios 0.19

パッケージをインストールする

まずはじめに、PHPで簡単に画像を操作できるパッケージ「Invervention」をインストールします。

以下のコマンドを実行してください。

coposer require intervention/image

※ 以前は「config/app.php」に登録をしていましたが、現在は不要になっています。
※ また、composer のバージョン2、早くなりましたね 🏃💨💨💨💨💨

PHPでWebpの操作に必要な要件

Webpの操作をするには、PHPimagewebp()という関数が必要になりますが、これは以下の要件が必要になりますので注意してください。

  • GDの場合: PHP 5.5以上
  • ImageMagic の場合:「libwebp」がコンパイルされている必要があります

アップロードするフォルダをつくる

今回は、アップロードした画像をWebpに変換しますが、IEのようにWebpをサポートしていないブラウザのために、予備のJPEG画像も保存しておくようにします。(自動切り替えできるようにします)

そのため、フォルダは「public/images/upload_images」に加えて、さらに中に2つフォルダを用意します。

※ なお、これらのファルダには書き込み権限をつけるのをわすれないようにしてください。

では、準備ができたので実際のコードを書いていきましょう❗

ルートをつくる

はじめに、ルートを作ります。
以下のコードを追加してください。

routes/web.php

<?php

// 省略

use \App\Http\Controllers\ImageUploadController;

Route::get('image_upload', [ImageUploadController::class, 'create']);
Route::post('image_upload', [ImageUploadController::class, 'store']);

コントローラーをつくる

続いて、コントローラーをつくります。
以下のコマンドを実行してください。

php artisan make:controller ImageUploadController

すると、ファイルが作成されますので、中身を以下のように変更します。

app/Http/Controllers/ImageUploadController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Str;

class ImageUploadController extends Controller
{
    public function create() {

        return view('image_upload.create');

    }

    public function store(Request $request) {

        $request->validate([
            'image' => ['required', 'image']
        ]);

        $result = false;
        $image_urls = [];

        try {

            $image = \Image::make($request->image); // 👈 ここでアップロードされた画像からインスタンスを作ってます
            $filename = 'upload_'. date('YmdHis') .'_'. Str::random(5);
            $image_types = ['jpg', 'webp'];

            foreach ($image_types as $image_type) {

                $upload_path = 'images/upload_images/'. $image_type .'/'. $filename .'.'. $image_type;
                $image_path = public_path($upload_path);
                $image->save($image_path); // 👈 ここで画像を保存してます

                $image_urls[$image_type] = url($upload_path); // 👈 これは return ようのURLです

            }

            $result = true;

        } catch (\Exception $e) {}

        return [
            'result' => $result,
            'image_urls' => $image_urls
        ];

    }
}

この中では、まずcreate()が実際にブラウザからアクセスすることになるメソッドで、ここで画像ファイルを選択することになります。

そして、一番重要なのが、store()です。

この中では、まずバリデーション(ただし今回のは最低限ですので、お好みで変更してください)をしています。

そして、送信データに問題がないことが確認できたら、次に\Image::make()で画像パッケージInterventionのインスタンスを作っています。

あとは、ループを使って必要なjpgwebp画像を各フォルダに保存しているだけです。

※ なお、画像のサイズ変更などその他の操作は以下のURLを参考にしてみてくだsだい。

📝 完全網羅!Intervention Image(PHP)で画像を編集する全実例

ビューをつくる

では、最後にビューです。
なお、ビューは説明が複雑にならないように2つに分けてご紹介します。

ファイルを選択してアップロードする部分

まず画像をアップロードする部分をつくります。

<html>
<head>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
</head>
<body>
<div id="app" class="p-4">
    <label class="btn btn-primary">
        画像を選択してアップロード
        <input class="d-none" type="file" accept="image/jpeg,image/png" @change="onFileChange">
    </label>
</div>
<script src="https://unpkg.com/vue@3.0.2/dist/vue.global.prod.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>

    Vue.createApp({
        methods: {
            onFileChange(e) {

              const files = e.target.files;

              if(files.length > 0) {

                  const file = files[0];
                  const url = '{{ route('upload_image.store') }}';
                  let formData = new FormData();
                  formData.append('image', file);

                  axios.post(url, formData)
                    .then(response => {

                        if(response.data.result === true) {

                            // アップロードが成功

                        }

                    })
                    .catch(error => {

                        console.log(error); // エラーの場合

                    });

              }

            }
        }
    }).mount('#app');

</script>
</body>
</html>

この中でやっているのは、まず<input type="file">changeイベントをセットし、ファイルが選択されたら、onFileChange()が実行されるようにしています。

そして、onFileChange()の中では、axiosを使って実際に画像をアップロードしますが、送信データにnew FormData()を使っていることに注意してください。

というのも、通常のやり方ではファイルデータを送信できないからです。

保存した画像を表示する部分

アップロード部分が完了したら、次は表示部分です。

冒頭部分でも書きましたが、現在Webpはほぼ全ての主要なブラウザでサポートされているので、単純に<img src="******">としてもいいのですが、IEなども考慮したい場合もあるかと思いますので、今回は「Webpをサポートしてる/してない」で自動的に切り替える「Vueコンポーネント」を作って表示してみます。

<html>
<head>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
</head>
<body>
<div id="app" class="p-4">

    <!-- 画像入力 -->
    <label class="btn btn-primary">
        画像を選択してアップロード
        <input class="d-none" type="file" accept="image/jpeg,image/png" @change="onFileChange">
    </label>
    <br>

    <!-- ⑤ 画像を表示 -->
    <v-webp
        :src="imageUrls.webp"
        :original-src="imageUrls.jpg">
    </v-webp>

</div>
<script src="https://unpkg.com/vue@3.0.2/dist/vue.global.prod.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>

    // ① コンポーネントをつくる
    const webpComponent = {
        props: ['src', 'original-src'],
        template: `
            <picture>
                <source :srcset="src" type="image/webp">
                <source :srcset="originalSrc" type="image/jpeg">
                <img :src="originalSrc">
            </picture>
        `
    };

    Vue.createApp({
        // ③ 変数を用意する
        data() {
            return {
                imageUrls: {
                    webp: '',
                    jpg: ''
                }
            }
        },
        methods: {
            onFileChange(e) {

              // 省略

              if(files.length > 0) {

                  // 省略

                  axios.post(url, formData)
                    .then(response => {

                        if(response.data.result === true) {

                            // アップロードが成功
                            this.imageUrls = response.data.image_urls; // ④ 返ってきた画像のURLをセット

                        }

                    })
                    .catch(error => {

                        console.log(error);

                    });

              }

            }
        }
    })
    .component('v-webp', webpComponent) // ② コンポーネントをセットする
    .mount('#app');

</script>
</body>
</html>

では、ひとつずつ見ていきましょう!

① コンポーネントをつくる

まずはコンポーネントの本体を定義します。
コンポーネント自体は特別な部分はありませんが、<picture> ... </picture> を使っている部分に注目してください。

この中に<source>タグや<img>タグをセットすることで、サポートされた画像形式のみが表示されることになります。

また、セットできるパラメータは、以下2つになります。

  • src: WebpのURL
  • original-src: JPGのURL

② コンポーネントをセットする

次に、定義したコンポーネントが実際に動くようにVue本体にセットします。

なお、「v-webp」の部分は<v-webp>...</v-webp>として使えるようにするための「タグ名」になりますが、ここはお好みで変更してOKです。

③ 変数を用意する &
④ 返ってきた画像のURLをセット

アップロードに成功するとLaravel側からは、webpjpgURLが返ってきますので、このURLを格納する変数を用意し、さらにそのURLを格納するコードを追加します。

⑤ 画像を表示

最後にVueコンポーネントを実行して、画像を表示する部分を追加します。

テストしてみる

では、実際に画像ファイルをアップロードしてみましょう❗

まずは、「http://*****/image_upload/create」へブラウザでアクセスし、ボタンをクリックして画像ファイルを選択します。

すると・・・・・・

はい❗
画像(Webp)が表示されました。

では、画像が本当に保存されたか見てみましょう。

こちらもうまく行っています。

そして、Ajax送信の結果とタグは次のとおりになります。

成功です😊✨

ダウンロードする

今回実際に開発したソースコード一式を以下からダウンロードすることができます。

Laravelで画像をwebpに対応させて軽量化する

※ CDNを使っているのでLaravelにセットしたらすぐ使えます。なお、フォルダの準備などはご自身で行ってください。

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回はLaravel + Vueで軽量画像Webpを作って表示してみました。

なお、一点気になったのは、Windows 10Ubuntuともに画像をダウンロードすると初期状態ではファイルを開くアプリが見つからず、結局ブラウザでしか表示できなかったことです。

おそらくWebpに対応している画像ビューアも存在しているのでしょうが、やはりまだウェブ上の環境が整ってきたもののローカルではサポートは多くないのかもしれません。

・・・とはいえ、通常はウェブ上の画像をダウンロードして後から見る機会はそれほど多くないとは思いますので、開発者としては選択肢のひとつにしていいと考えています。

ぜひ皆さんもWebpに触れてみてくださいね。(画像の劣化はほぼ見られませんよ👍)

ではでは〜❗

「お笑い芸人・東野さんの影響で、
【宇宙よりも遠い場所】を視聴。
感動ウルウル😭」

このエントリーをはてなブックマークに追加       follow us in feedly