サイトを軽量化!Laravelでwebp形式の画像をつくる&使う

こんにちは❗フリーランス・エンジニアの 九保すこひ です。

さてさて、突然ですがみなさん「webp(うぇっぴー)」って聞いたことあるでしょうか。

webpとは、軽量の新画像形式(jpegpngなどの仲間)のことで、なんと、開発したグーグルによると、可逆形式ではpng26%、不可逆形式では25〜34%もファイルサイズを減らすことができると紹介されています

そのため、たくさん画像を使うサイトでは高速化のためにwebpを使うことが有効といっていいんですが、実はそれほど普及しているイメージはなかったりもします。

なぜかというと、Can I useを見てもらえば分かりやすいですがIESafariwebpをサポートしていないからなんですね(うーん、大人の事情もからんでいそうですね・・・😫)

ただ、それでもブラウザのシェアを考えるとGoogle Chromeが圧倒的なので、画像をメインにしているサイトだとブラウザによってwebpJPEGなどその他の画像形式を切り替えることで、サイトの高速化と負荷軽減を実現することができます。

そこで!

今回は、Laravelにアップロードされた画像からwebpjpeg形式の2画像をつくり、ブラウザによって自動的に切り替えをする方法をご紹介します。

ぜひ皆さんのお役に立てると嬉しいです😊✨

開発環境: Laravel 6.x、Vue 2.6

前提として

GDを使う場合、以下のようにwebpがサポートされていると表示されている必要があります。(PHP 5.5以上か7

Imagemagickの場合はlibwebpがコンパイルされている必要があります。

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

実際にコードを書いていく前に、PHPで画像を簡単に操作できる有名パッケージinterventionをインストールしておきましょう。

composer require intervention/image

※ なお、interventionのより詳しい使い方は完全網羅!Intervention Image(PHP)で画像を編集する全実例を参考にしてみてください。

Laravelに画像をアップロードする機能をつくる

では、ここからが実際にプログラムを書いていく作業になります。

ルートをつくる

まずはルートをつくります。
routes/web.phpを開いて以下を追加してください。

Route::get('webp/create', 'WebpController@create');
Route::post('webp', 'WebpController@store');

コントローラーをつくる

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

php artisan make:controller WebpController

すると、app/Http/Controllers/WebpController.phpというファイルが作成されるので、中身を以下のように変更してください。

<?php

namespace App\Http\Controllers;

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

class WebpController extends Controller
{
    public function create() {

        return view('webp/create');

    }

    public function store(Request $request) {

        $result = false;

        if($request->hasFile('image')) {

            $path = $request->image->path();
            $image = \Image::make($path);
            $uuid = Str::uuid();

            // Webpを作成
            $webp_path = public_path('images/webp/'. $uuid .'.webp');
            $image->save($webp_path);

            // JPEGを作成
            $jpeg_path = public_path('images/jpeg/'. $uuid .'.jpg');
            $image->save($jpeg_path);

            $result = true;

        }

        return ['result' => $result];

    }
}

この中で重要なのがstore()です。

このメソッドではAjaxを通して送信されてくる画像を取得し、それぞれwebpjpeg形式の画像を作成しています。

なお、保存されるフォルダはpublic/images/webppublic/images/jpegですが、今回はテストなのでこれらのフォルダには777(全てOK)の権限を与えています。

ただ、これはセキュリティ上良いとはいえないので、気になる方はstorageをpublic化しておくことをおすすめします。

※ また、バリデーションもついていませんのでご注意ください。

ビューをつくる

最後に画像をアップロードするフォームのビュー(HTML)を作成します。
resources/views/webp/create.blade.phpというファイルを作成して中身を以下のように変更してください。

<html>
<head>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="p-4">
    <div id="app" class="container">
        <div class="row">
            <div class="col-4">
                <div class="form-group">
                    <input class="form-control" type="file" accept="image/*" @change="onFileChange">
                </div>
                <button class="btn btn-primary" type="button" @click="onSubmit">画像をアップロードする</button>
            </div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
    <script>

        new Vue({
            el: '#app',
            data: {
                imageFile: null
            },
            methods: {
                onFileChange(e) {

                    // 選択された画像を変数で保持する
                    this.imageFile = e.target.files[0];

                },
                onSubmit() {

                    // 画像をアップロード
                    const url = '/webp';
                    let formData = new FormData();
                    formData.append('image', this.imageFile);

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

                            if(response.data.result) {

                                alert('アップロードが完了しました。');

                            }

                        });

                }
            }
        });

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

この中で重要なのは、まず画像が選択されたときに実行されるonFileChange()です。

このメソッドの中では、選択された画像ファイルの情報をいったんimageFileという変数の中に格納しています。

そして、次に「画像をアップロードする」ボタンをクリックしたときに実行されるonSubmit()です。

選択された画像は、axiosAjax送信することになりますが、通常の入力値を送信する書き方とは違ってFormDataを使っていることに注意してください。

FormDataへのデータ追加はappend()を使います。

これで、画像ファイルを送信すると自動的にwebpjpeg画像が作成されるようになりました。

なお、webpのファイルサイズがどれだけ圧縮できるかをチェックしたかったので以下のpng画像をアップロードして試してみました


(ファイルサイズは、92.8 KB)

結果としては、

  • jpeg ・・・ 58.5 KB
  • webp ・・・ 29.4 KB

となりました。

なんと、Webpの方はJPEGファイルの半分ほどのサイズです😳
これは高速化には大きな影響があると思われます。

webpとjpegを切り替えて表示する

では、ここからはアップロードされた画像(webp, jpeg)をブラウザによって切り替えて表示する方法をご紹介します。

HTML 5を使う

以下のようにHTML 5<picture>タグを使うことで、ブラウザがwebp対応している場合はwebpを、そうでない場合はjpegなど他の形式の画像を読み込むというように自動的に切り替えることができます。

<picture>
    <!-- WebpのURL -->
    <source type="image/webp" srcset="/images/webp/*****.webp">
    <!-- JpegのURL -->
    <img src="/images/jpeg/*****.jpg">
</picture>

コンポーネントをつくる

ただし、毎回毎回先ほどのタグを作成するのは面倒だったりします。
そこで、次のようなコンポーネントをつくっておくと便利でしょう。

Bladeテンプレート

resources/views/components/webp.blade.phpというファイルを作成して中身を以下のように変更します。

<picture>
    <source type="image/webp" srcset="{{ $src }}">
    <img src="{{ $alt }}">
</picture>

使い方はこのようになります。

@component('components.webp')
    @slot('src', '/images/webp/*****.webp')
    @slot('alt', '/images/jpeg/*****.jpg')
@endcomponent

Vueコンポーネント

Vueコンポーネントの場合は次のようになります。

Vue.component('v-webp', {
    props: ['src', 'alt'],
    template: '<picture>'+
        '<source type="image/webp" :srcset="src">'+
        '<img :src="alt">'+
        '</picture>'
});

使い方はこのようになります。

<v-webp
    src="/images/webp/*****.webp"
    alt="/images/jpeg/*****.jpg"></v-webp>

これで自動切り替えが簡単になると思います。
お疲れ様でした😊✨

おわりに

ということで今回は軽量な画像形式のwebpをつくる&使う方法をご紹介しましたが、実際にwebpの画像がいかに軽量になるかを知ることになりました。

ただ、webpはまだまだメジャーになるには道半ばで、windows 10Ubuntu 18.04で確認しましたが、通常の画像ソフトでは見ることができずGoogle Chromeで開く必要がありました。

そのため、インターネット上には「webpをjpegに変換する」というオンライン・サービスもあったりするようです。

とはいっても、フリーの画像ソフトGimpwebpをサポートしたという動きもありますし、なによりGoogleが開発したというのは大きいかもしれません。(たまにGoogleは「はい、やめまーす♪」ってなるのも事実ですが😂)

ということでページの高速表示のためにwebpも検討してみてはいかがでしょうか。

ではでは〜♪

今回の技術をつかった開発のご依頼、お待ちしております😊✨ お問い合わせ また、個人レッスンや、わかりにくい部分がありましたらからお気軽にご連絡ください。 どうぞよろしくお願いいたします!
このエントリーをはてなブックマークに追加       follow us in feedly