
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、皆さんは「Webp」(うぇっぴー)という画像形式を聞いたことがあるでしょうか。
Webp
とは、グーグルが作った軽量な画像形式のことで、
JPEGと比べると平均30%もファイルサイズが減る
と言われています!
ただ、このWebp
、実は2010年にリリースましたが、私も含めてあまり採用している人は見かけませんでした。
なぜなら、
当時はGoogle Chromeだけしか表示できなかったから
です。
そうです。IE
は当然として(ゴメンナサイ)、Firefox
、Safari
でもサポートされていなかったので、「軽いのはわかるけど、表示できないブラウザがあったら、使えないでしょ…」となっていました。
しかし、その後状況は一変します。
2019年にFirefox
でサポートされると、2020年にはSafari
でも表示が可能になり、さらにその傍らでIE
がほぼ消滅&Edge
がChromium
ベースになったことで、2020年12月現在ではなんと主要なブラウザはほぼ全てWebp
をサポートしているようになりました。
そこで
今回は、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
の操作をするには、PHP
のimagewebp()
という関数が必要になりますが、これは以下の要件が必要になりますので注意してください。
- 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
のインスタンスを作っています。
あとは、ループを使って必要なjpg
とwebp
画像を各フォルダに保存しているだけです。
※ なお、画像のサイズ変更などその他の操作は以下の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
側からは、webp
とjpg
のURL
が返ってきますので、このURL
を格納する変数を用意し、さらにそのURLを格納するコードを追加します。
⑤ 画像を表示
最後にVueコンポーネント
を実行して、画像を表示する部分を追加します。
テストしてみる
では、実際に画像ファイルをアップロードしてみましょう
まずは、「http://*****/image_upload/create」へブラウザでアクセスし、ボタンをクリックして画像ファイルを選択します。
すると・・・・・・
はい
画像(Webp)が表示されました。
では、画像が本当に保存されたか見てみましょう。
こちらもうまく行っています。
そして、Ajax送信の結果とタグは次のとおりになります。
成功です
ダウンロードする
今回実際に開発したソースコード一式を以下からダウンロードすることができます。
Laravelで画像をwebpに対応させて軽量化する※ CDNを使っているのでLaravelにセットしたらすぐ使えます。なお、フォルダの準備などはご自身で行ってください。
おわりに
ということで、今回はLaravel + Vue
で軽量画像Webp
を作って表示してみました。
なお、一点気になったのは、Windows 10
、Ubuntu
ともに画像をダウンロードすると初期状態ではファイルを開くアプリが見つからず、結局ブラウザでしか表示できなかったことです。
おそらくWebpに対応している画像ビューアも存在しているのでしょうが、やはりまだウェブ上の環境が整ってきたもののローカルではサポートは多くないのかもしれません。
・・・とはいえ、通常はウェブ上の画像をダウンロードして後から見る機会はそれほど多くないとは思いますので、開発者としては選択肢のひとつにしていいと考えています。
ぜひ皆さんもWebp
に触れてみてくださいね。(画像の劣化はほぼ見られませんよ)
ではでは〜
「お笑い芸人・東野さんの影響で、
【宇宙よりも遠い場所】を視聴。
感動ウルウル」