深掘り spatie/laravel-medialibrary - 画像の変換
の続きっちゃ続き
今回利用するもののdeploy
弊githubリポジトリよりspatie-lib-demo
ブランチを...
git clone -b spatie-lib-demo https://github.com/catatsumuri/laravel-inertia-stack-ja.git
今回はlaravel sail
...ではなく、すなわちdockerとかの環境ではない素のdebianに導入した。ライブラリーは最低限しか入っていない状態でのテスト。
画像のconversion
これは
/*
* The engine that should perform the image conversions.
* Should be either `gd` or `imagick`.
*/
'image_driver' => env('IMAGE_DRIVER', 'gd'),
の設定でわかるように、gd
かimagick
を導入する必要がある。初期状態ではgd
が選ばれるのでこの段階でgdドライバーがないとエラーになる。
なお、ここではgd
もimagick
も検証してみよう
# apt install php-gd php-imagick
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
fonts-droid-fallback fonts-noto-mono fonts-urw-base35 ghostscript gsfonts
imagemagick-6-common libavahi-client3 libavahi-common-data libavahi-common3
libcups2 libfftw3-double3 libgs-common libgs10 libgs10-common libidn12
libijs-0.35 libjbig2dec0 liblcms2-2 liblqr-1-0 libltdl7
libmagickcore-6.q16-6 libmagickwand-6.q16-6 libopenjp2-7 libpaper-utils
libpaper1 libwebpdemux2 libwebpmux3 php8.2-gd php8.2-imagick poppler-data
さらに exif
パッケージも導入し、調査してみる
exifの状態
このようにいかにも位置情報が付いていそうな鉄っぽい画像をアップロードした
# find storage/app/public/3/
storage/app/public/3/
storage/app/public/3/conversions
storage/app/public/3/conversions/2024年06月02日_14時58分32秒-thumb.jpg
storage/app/public/3/conversions/2024年06月02日_14時58分32秒-original.jpg
storage/app/public/3/2024年06月02日_14時58分32秒.jpg
現在このように id=3
という状態でアップロードされており、exifを検証すると
$ exif storage/app/public/3/2024年06月02日_14時58分32秒.jpg
EXIF tags in 'storage/app/public/3/2024年06月02日_14時58分32秒.jpg' ('Motorola' byte order):
--------------------+----------------------------------------------------------
Tag |Value
--------------------+----------------------------------------------------------
Manufacturer |Apple
Model |iPhone 12 mini
Orientation |Top-left
X-Resolution |300
Y-Resolution |300
Resolution Unit |Inch
Software |17.1.2
Date and Time |2024:06:02 14:58:32
YCbCr Positioning |Centered
Compression |JPEG compression
X-Resolution |72
Y-Resolution |72
Resolution Unit |Inch
Exposure Time |1/284 sec.
F-Number |f/1.6
Exposure Program |Normal program
ISO Speed Ratings |32
Exif Version |Exif Version 2.32
Date and Time (Origi|2024:06:02 14:58:32
Date and Time (Digit|2024:06:02 14:58:32
Offset Time For Date|+09:00
Offset Time For Date|+09:00
Offset Time For Date|+09:00
Components Configura|Y Cb Cr -
Shutter Speed |8.15 EV (1/284 sec.)
Aperture |1.36 EV (f/1.6)
Brightness |6.47 EV (304.51 cd/m^2)
Exposure Bias |0.00 EV
Metering Mode |Pattern
Flash |Flash did not fire, compulsory flash mode
Focal Length |4.2 mm
Subject Area |Within rectangle (width 2208, height 1387) around (x,y) =
Maker Note |1535 bytes undefined data
Sub-second Time (Ori|898
Sub-second Time (Dig|898
FlashPixVersion |FlashPix Version 1.0
Color Space |Uncalibrated
Pixel X Dimension |1512
Pixel Y Dimension |2016
Sensing Method |One-chip color area sensor
Scene Type |Directly photographed
Exposure Mode |Auto exposure
White Balance |Auto white balance
Focal Length in 35mm|26
Scene Capture Type |Standard
Lens Specification |1.550000, 4.2, 1.6, 2.4
Lens Make |Apple
Lens Model |iPhone 12 mini back dual wide camera 4.2mm f/1.6
Composite Image |2
North or South Latit|N
Latitude |35, 10, 13.48
East or West Longitu|E
Longitude |136, 52, 54.01
Altitude Reference |Sea level
Altitude |2.23306
GPS Time (Atomic Clo|05:58:31.00
Speed Unit |K
Speed of GPS Receive|0.6900000
GPS Image Direction |T
GPS Image Direction |34.4986
Reference for Bearin|T
Bearing of Destinati|34.4986
GPS Date |2024:06:02
GPS Horizontal Posit|8.12382
--------------------+----------------------------------------------------------
EXIF data contains a thumbnail (11514 bytes).
このようになっている。ここでセンシティブな値と言えばもちろん
North or South Latit|N
Latitude |35, 10, 13.48
East or West Longitu|E
Longitude |136, 52, 54.01
この辺がとりわけセンシティブな値であろう。
% exif storage/app/public/3/conversions/2024年06月02日_14時58分32秒-original.jpg
Corrupt data
The data provided does not follow the specification.
ExifLoader: The data supplied does not seem to contain EXIF data.
original
として保存した画像からはこれがトリミングされているというか、GD
にはexifを引き継ぐ機能がない(んだと思う、こういう知識は実際専門外だけど)
imagickで同様の処理をするとexifは残留する
/*
* The engine that should perform the image conversions.
* Should be either `gd` or `imagick`.
*/
// 'image_driver' => env('IMAGE_DRIVER', 'gd'),
'image_driver' => env('IMAGE_DRIVER', 'imagick'),
などとして同様の処理を施してみると
exif storage/app/public/4/conversions/2024年06月02日_14時58分32秒-original.jpg
EXIF tags in 'storage/app/public/4/conversions/2024年06月02日_14時58分32秒-original.jpg' ('Motorola' byte order):
--------------------+----------------------------------------------------------
Tag |Value
--------------------+----------------------------------------------------------
Manufacturer |Apple
Model |iPhone 12 mini
Orientation |Top-left
X-Resolution |300
....<省略
このようにconversionした画像にexifが残る。
jpegoptimを導入して自動最適化機能によりexifを削除するようにする
これを解消するには jpegoptim
を導入するだけ。これでspatie/laravel-medialibrary
はこのコマンドを探して自動起動してexifを取り除いてくる
apt install jpegoptim
exifが消滅した
この辺に関しては https://spatie.be/docs/laravel-medialibrary/v11/converting-images/optimizing-converted-images を参照。
いずれにしたって、installドキュメントなどで外部ツールに依存している事を明確にして正しくデプロイするようにする。いくつもインスタンスをインストールする場合はこういうのを人力でやると失敗いしそうですね。
画像のリサイズ方法
たとえば、この鉄の画像は1512x2016
という解像度だ。これを何も考えず600x600指定にすると
$this->addMediaConversion('thumb')
->width(600)
->height(600)
->nonQueued();
450x600
このように縦横をうまいこと合わせてくれるんだけど、ここでのコードの意図とはちょっと違ってきますよね。600,600にジャストフィットするようにするには
use Spatie\Image\Enums\Fit;
// ...
$this->addMediaConversion('thumb')
->fit(Fit::Crop, 600, 600)
->nonQueued();
とすると
600x600
となる。このあたりは以下のドキュメントにある
いろいろやる
fit(Fit::Crop, 300, 600)
$this->addMediaConversion('thumb')
->fit(Fit::Crop, 300, 600)
->nonQueued();
300x600、かなり縦長になった
fit(Fit::Contain, 300, 300)
$this->addMediaConversion('thumb')
->fit(Fit::Contain, 300, 300)
->nonQueued();
アスペクト比を維持しつつ最大サイズに収める(余白あり)。この場合縦横どちらか最大300
fit(Fit::Max, 400, 400)
$this->addMediaConversion('thumb')
->fit(Fit::Max, 400, 400)
->nonQueued();
Contain と同じだが、小さい画像は拡大しない。これは例が悪い(元が小さくない)のでよくわからんすね。試してみてください
fit(Fit::Fill, 600, 600)
$this->addMediaConversion('thumb')
->fit(Fit::Fill, 600, 600)
->background('#ffffff')
->nonQueued();
余白を指定色で埋める
fit(Fit::Stretch, 600, 600)
$this->addMediaConversion('thumb')
->fit(Fit::Stretch, 600, 600)
->nonQueued();
強引に述ばす。ほぼ使えないと思う
いずれにせよ、用途にあった最適なリサイズ方法を選択する必要がある。avatarなどはスクウェアがいいでしょうしね。
PDFのサムネイル
ここではPDFをアップロードする。前回のコードではvalidationを何も考えてないのでアップロードできる。この段階ではもちろん
このようにサムネイルを生成できないのでエラーになっている。しかしspatieではpdfのサムネイルを生成する事が可能である。
apt install imagemagick ghostscript
する。しかし
となった場合は
<!-- disable ghostscript format types -->
<!--
<policy domain="coder" rights="none" pattern="PS" />
<policy domain="coder" rights="none" pattern="PS2" />
<policy domain="coder" rights="none" pattern="PS3" />
<policy domain="coder" rights="none" pattern="EPS" />
<policy domain="coder" rights="none" pattern="PDF" />
<policy domain="coder" rights="none" pattern="XPS" />
-->
この辺をコメントアウトしてwebサーバーを再起動すると読めるようになるかも。 サンプルのpdf をアップロードすると
なお、ページ指定なども可能である。っていっても2ページ目を指定して2ページ目がなかったらどうなるんでしょ。面倒なのでやってません。
動画ファイルからのサムネイル生成
php-ffmpeg/php-ffmpeg
を入れておくとサムネイルが生成される
このようになった場合は
sudo apt install ffmpeg
としてffmpeg
を導入しておく。
からダウンロードしたファイルをアップロードしてみると
このようになる
$this->addMediaConversion('thumb')
->fit(Fit::Crop, 400, 400)
->nonQueued();
PDFなんてもんは大抵1枚目を表示しておけば事足りるはずなのであるが、動画の場合は割とそういうわけにもいかない事がある。そのような時は
$this->addMediaConversion('thumb')
->fit(Fit::Crop, 400, 400)
->extractVideoFrameAtSecond(2) // 2秒後のフレームを取得
->nonQueued();
2秒後の画像が取得できた
以上、主にconversionに関して見てきた
なお、(本当の)オリジナルファイルをlocalに、conversionをS3
に、といった複雑な事もできるっちゃできるので色々やってみたい方はドキュメントをあたってみてくださいなと。
このライブラリーに関してはもう少しいろいろ語るポイントがあるので、あと1回はやりたい
Discussion