やること
実現したい内容
作成しているサービスが低コストで運用していきたいため、フォームからPOSTで送られてきた画像をController内でサイズ変更してから保存したいと思いました。
方法
オープンソースのPHP画像処理および操作ライブラリであるInterventionImageを使用していきます。
Laravel(PHP)で画像処理といえばこれって感じのライブラリのようです。
環境
Dockerで作成したローカル環境で実施
- PHP: 8.1.18
- Laravel10
- nginx: 1.24.0
- mysql: 8.0
やったこと
InterventionImageをインストール
composerを使ってインストールします。
composer require intervention/image
インストールすると、composer.jsonとcomposer.lockが更新されます。
サービスプロバイダとエイリアスを登録しておきます。
'providers' => ServiceProvider::defaultProviders()->merge([
...中略
Intervention\Image\ImageServiceProvider::class,
])->toArray(),
'aliases' => Facade::defaultAliases()->merge([
'Image' => Intervention\Image\Facades\Image::class,
])->toArray(),
InterventionImageのインストールはこれだけで完了です。
Controllerでサイズ変更する
use Image;
public function store(Request $request)
{
$itemPost = new ItemPost();
...中略
if (request('image')) {
$name = date('Ymd_His') . '_' . request()->file('image')->getClientOriginalName();
$image = request()->file('image');
$image = Image::make($image);
// 圧縮
$image->resize(
300,
null,
function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
}
)->orientate();
$image->save('storage/images/' . $name, 60);
$itemPost->image = $name;
}
$itemPost->save();
}
上記でサイズ変更に成功しました。
内容を説明していきます。
$name = date('Ymd_His') . '_' . request()->file('image')->getClientOriginalName();
requestで受け取った画像の名前に日付を追加した文字列をファイル名として$nameに格納しておきます。
画像処理には関係ないので省略しますが、最後のところで$itemPostをDBに保存しています。
$image = request()->file('image');
$imageにリクエストで受け取ったファイルを格納します。
$image = Image::make($image);
use宣言したImageを利用して、InterventionImageを使用しています。
make関数でリクエストの画像からイメージインスタンスを作成しています。
$image->resize(
300,
null,
function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
}
)->orientate();
resize関数でサイズを変更しています。
横幅を300pxにリサイズしています。aspectRatioを設定することで縦横比は原本のままにしています。upsizeは画像が拡大されないようにしています。
orientate関数はEXIF画像プロファイル設定「方向」を読み取り、画像を回転して画像を正しく表示するメソッドになります。使用することで勝手に画像が反転する現象を解消しております。後述しますが、orientateを使用する場合はexifサポートを有効にする必要があります。
$image->save('storage/images/' . $name, 60);
イメージインスタンスをstorage/images/ファイル名 のパスに品質「60」で保存しています。
上記で、storage/images/ファイル名 のパスに画像処理後の画像が保存される形になります。
トラブルシューティング
エラー1: InterventionImageを使用するにはGDサポートを有効にする必要がある
GD Library extension not available with this PHP installation. というエラーが発生しました。
GDがインストールされていませんというエラーになります。GDをインストールすることで改善されました。
GDのインストール: https://www.php.net/manual/ja/image.installation.php
具体的には、Dockerを使っているので、以下をPHPのDockerfileに追加しました。
RUN apt-get install -y zlib1g-dev \
libzip-dev \
libjpeg-dev \
libpng-dev \
libfreetype6-dev \
libjpeg62-turbo-dev
RUN docker-php-ext-install pdo_mysql zip
RUN docker-php-ext-configure \
gd --with-freetype --with-jpeg
GDがJPEG等のフォーマットを扱えるようにオプションを指定しています。
エラー2: exifサポートが有効になっていない
Reading Exif data is not supported by this PHP installation. というエラーが発生しました。
画像が勝手に回転してしまう現象を改善するために$image->orientate()
を使用しました。
orientateを利用するためにexifサポートを有効にする必要がある。
PHPのDockerfileに以下を設定することで改善しました。
RUN docker-php-ext-configure \
gd --with-freetype --with-jpeg
# 以下を追加
RUN docker-php-ext-install -j$(nproc) gd exif
(参考)1MB以上の画像がフォームから送信できない場合
413 Request Entity Too Large というエラーが出た時
以下3つのデータサイズが設定されているか確認する。
- nginx・設定ファイルのclient_max_body_size(もしあるなら変更、なければ追加)
- php.iniのpost_max_size
- php.iniのupload_max_filesize
自分はnginxのdefault.confに下記を追加して改善した。
client_max_body_size 64m;
参考: https://blog.capilano-fw.com/?p=256
まとめ
初めてInterventionImageを使用したので、ちょこちょこエラーが出てしまいましたが、とても簡単に画像加工できることが分かりました。ドキュメントも関数ごとにまとまっていて見やすく、モザイク処理やテキスト挿入などできることは多いので、他のサービスを作る時に画像処理が必要だった場合は、また利用したいと思いました。