Nextcloudの内部画像処理を軽くしてサクサク表示させる (3) プレビュー画像を減らす

2022-10-13
  • B!

Nextcloud

NextcloudのPreview Generatorアプリを導入することで、プレビュー画像を事前に作成する方法で画面がサクサク表示できるようになることを前回紹介した。

一方でこの手法には問題もあり、プレビュー画像が大量すぎてストレージを圧迫する問題も説明した。

現状、生成されるプレビュー画像の最大サムネイル解像度の設定を1024 x 1024 pixに設定すると、一枚の画像から8枚ものプレビューが作成されることがわかっている。

-rw-r--r--  1 www  www   126K Jan  6 15:43 1024-768-max.jpg
-rw-r--r--  1 www  www    99K Jan  6 15:43 768-768-crop.jpg
-rw-r--r--  1 www  www    21K Jan  6 15:43 341-256.jpg
-rw-r--r--  1 www  www    17K Jan  6 15:43 256-256-crop.jpg
-rw-r--r--  1 www  www    14K Jan  6 15:43 256-192.jpg
-rw-r--r--  1 www  www   2.8K Jan  6 15:43 85-64.jpg
-rw-r--r--  1 www  www   2.3K Jan  6 15:43 64-64-crop.jpg
-rw-r--r--  1 www  www   1.9K Jan  6 15:43 64-48.jpg

写真愛好家に比べると決して多い枚数ではないが、ストレージに保存している写真と動画ファイルは7万ファイルあるため、8種類ものプレビュー画像を生成されると 7万枚 x 8種類 = 56万ファイルものプレビュー画像が事前生成されてしまう。ここまでくるとさすがにに量が多すぎて仮想マシンで動かすには苦しくなってくる。

このような問題のため、プレビュー画像を減らしつつ画面表示の速さも犠牲にしない方法を探ってみた。

目次

目標値を考える

どれぐらいの枚数にプレビュー画像を減らせば良いのだろうか。なぜかというと、プレビュー画像の解像度別のバリエーション数は単に減らせば良いというものではないからだ。私は写真共有サービスを作った経験もあるので傾向を書くと、ソーシャルメディアや画像アセットを大量に扱うWebサービスを開発するにあたって、最適なプレビュー画像のバリエーション数の設計はトレードオフもあり結構難しい。プレビュー画像の解像度別バリエーションを増やしたときのメリットとデメリットをざっくりまとめると、こちらの表のようになる。

プレビューのバリエーション数 ストレージ消費 転送量 受信速度 ブラウザキャッシュ
少ない 少ない 多い 遅め よく効く
多い 多い 少ない 早め 効きにくい

つまり、プレビューのバリエーション数を減らす作戦を取った場合は、大きめのプレビュー画像を共通項として用意して、クライアントサイドのブラウザ側のCSSで適切な大きさに縮小して表示することになる。利点はサーバサイドの負担が減る点。また、クライアント側は同じプレビュー画像を使い回して表示するため、ブラウザキャッシュが効く範囲では結構表示が速くなる。一方で、欠点はウェブサーバは大きめのプレビュー画像を送出するので、データ送出量で従量課金されるAWSなどで運用すると金がかかる。

一方で、プレビューのバリエーションを大量に作る作戦では逆のメリット・デメリットとなる。

どの要素も程度問題で、取り扱い画像数が何万枚を超えると転送量の方がキツくなるとか環境で条件が変わってくる。

それで、今回の環境ではどうするか

このようなことを考えつつ、今回の環境ではできるだけプレビューの枚数を減らして転送量は気にしない作戦とした。理由は用途が自宅内のNASで、使用環境は仮想マシンで通信はLANで限定である。したがって通信量はいくらでも気にする必要はなく、仮想マシンで非力であることの方の問題が大きいからである。

プレビュー画像は2種類まで減らすことが可能

結論から書くと、以前は8種類あったプレビュー画像は工夫すると2種類まで減らすことが可能だ。ここまで減れば効果は絶大で、表示の高速化とプレビュー画像の事前作成の軽量化の両方が実現でき、本当にサクサクになる。

2種類のバリエーションのプレビュー画像

チューニングの結果、生成されるプレビュー画像の解像度は次のような2種類だけとなった。
-rw-r--r--  1 www  www  75838 Jan 28  2022 1024-681-max.jpg
-rw-r--r--  1 www  www  66836 Jan 28  2022 681-681-crop.jpg
  • 1枚目は縦横1024pixの枠に収まるサイズに縮小したプレビュー画像。元の写真の縦横比を引き継ぐのでこの例では1024 x 681 pix
  • もう一枚は、上記のプレビューを正方形でクロップしたもの。短手方向の辺の長さで正方形にするので、681 x 681 pixとなる。
Nextcludへは、この2枚のプレビューを使い回して各画面をWeb表示するように設定する。

2種類のプレビュー画像だけを使い回すように設定をするには

はじめにNextcloud本体がどのようなタイミングでプレビュー画像を生成するのかを理解する必要がある。Nextcloudのweb画面のhtmlソースを見ると、プレビュー画像のimgタグではこのようなURLで画像を表示させている。
https://hoge.com/core/preview?fileId=92282&x=500&y=500
/core/previewというPHPへ収容画像のfile idを指定し、x=, y=で欲しいプレビュー画像の解像度を指定すると、適切な画像が送出される。そしてここで指定する解像度の数値(x=,  y=)によってプレビュー画像生成と再利用の挙動が以下のように変化する。
  • 初期設定の最大解像度より大きな画像がリクエストされた場合は、既存のプレビュー画像ファイルの中で一番大きな画像を再利用するか、最大解像度のプレビューを新規作成する
  • 初期設定の最大解像度より小さな画像をリクエストされた場合は、既存のプレビュー画像ファイルにリクエスト解像度の2倍以内のものがあれば、リクエストよりも少々大きい画像となるがプレビュー画像ファイルを再利用してそのまま送出。しかし該当するプレビュー画像がない場合は、リクエストされた解像度のプレビュー画像を新規生成する
文章にすると長くなってしまうが、このような挙動となる。簡単にいうと、プレビュー画像の解像度をリクエストされたとき、既存のプレビュー画像ファイルに近しい解像度のものがすでに存在していればその画像を送出してしまう。しかし、近しい解像度の画像がない場合は、新たにプレビュー画像を生成する。

ここから先は、以下のような2種類のプレビュー画像を極力再利用するようNextcloudを設定する。
-rw-r--r--  1 www  www  75838 Jan 28  2022 1024-681-max.jpg
-rw-r--r--  1 www  www  66836 Jan 28  2022 681-681-crop.jpg

実際の設定

Preview Generatorの改善

現在、Preview Generatorは8種類のプレビューを生成する。
-rw-r--r--  1 www  www   126K Jan  6 15:43 1024-768-max.jpg
-rw-r--r--  1 www  www    99K Jan  6 15:43 768-768-crop.jpg
-rw-r--r--  1 www  www    21K Jan  6 15:43 341-256.jpg
-rw-r--r--  1 www  www    17K Jan  6 15:43 256-256-crop.jpg
-rw-r--r--  1 www  www    14K Jan  6 15:43 256-192.jpg
-rw-r--r--  1 www  www   2.8K Jan  6 15:43 85-64.jpg
-rw-r--r--  1 www  www   2.3K Jan  6 15:43 64-64-crop.jpg
-rw-r--r--  1 www  www   1.9K Jan  6 15:43 64-48.jpg
64 x 64 pixを起点に最大解像度指定サイズまで段階的な解像度画像を生成するように動作している。これを2枚だけ生成するように改修する。
apps/previewgenerator/lib/SizeHelper.php を改造する。ループ内の解像度の種類追加処理を2ヶ所コメントアウトする。
        $w = 32;
        while ($w <= $maxW) {
//            $sizes['width'][] = $w;
            $w *= 2;
        }


        $h = 32;
        while ($h <= $maxH) {
//            $sizes['height'][] = $h;
            $h *= 2;
        }
プレビュー画像の解像度設定を変更する。
% php ./occ config:system:set preview_max_x --value 1024
% php ./occ config:system:set preview_max_y --value 1024
% php ./occ config:system:set jpeg_quality --value 70
% php ./occ config:app:set preview jpeg_quality --value=“70"

% php ./occ config:app:set previewgenerator squareSizes --value=“1024"
% php ./occ config:app:delete previewgenerator widthSizes
% php ./occ config:app:delete previewgenerator heightSizes
以上で、Preview Generatorが生成するプレビューは2種類だけになる。

Recommendationアプリの停止

Nextcloudのメイン画面を開くと、必ず画面上部に新着ファイルが6個表示される。
main

このRecommendation表示エリアは、はっきり言って役に立たない。新着ファイルが表示されるくせに更新日は表示されないし、画面の多くのスペースを食って肝心のファイル一覧表示が狭くなるので邪魔なだけである。
さらに、このアプリは64 x 64 pixという専用サイズのプレビューを新規生成する。非常に小さなプレビュー画像なので、既存のプレビュー画像ファイルを使い回しせずに次々とファイルが新規作成されてしまうのだ。

ということで、役に立たないし余計なプレビュー画像ファイルをサーバに溜め込むだけなので、このRecommendationアプリは設定から停止させた。停止されると表示されなくなり、v16とか昔のNextcloudのようにすっきりとした画面になる。

Photosアプリの改修

このアプリを動作させると256 x 256 pixのプレビュー画像を新規生成するので効率が悪い。Preview Genatorで事前生成した500 x 500 pix画像を使い回すように改造する。
photos app

Javascript中に記載されている解像度指定パラメータを変更する。ビルド済みのJSで1行に圧縮されているスクリプトなので、sedコマンドをうまく使って書き換える。

環境はNextcloud 24.0.5
apps-pkg/photos/js 以下の

photos-src_mixins_GridConfig_js-src_utils_CancelableRequest_js-src_components_EmptyContent_vue-src_c-45f6cf.js
の
concat(this.decodedEtag,"&x=",250,"&y=",250,"&forceIcon=0&a=").concat(
解像度250pix指定を500pixへ変更する。

リネーム&バックアップ
% mv photos-src_mixins_GridConfig_js-src_utils_CancelableRequest_js-src_components_EmptyContent_vue-src_c-45f6cf.js photos-src_mixins_GridConfig_js-src_utils_CancelableRequest_js-src_components_EmptyContent_vue-src_c-45f6cf.js.org
sedで置き換えながら複製
% sed -e 's/x=",250,"&y=",250,/x=",500,"\&y=",500,/' photos-src_mixins_GridConfig_js-src_utils_CancelableRequest_js-src_components_EmptyContent_vue-src_c-45f6cf.js.org > photos-src_mixins_GridConfig_js-src_utils_CancelableRequest_js-src_components_EmptyContent_vue-src_c-45f6cf.js


もう一つファイルを修正
photos-src_views_Albums_vue.js

concat(e,"&x=",250,"&y=",250,"&forceIcon=0&a=0"
解像度指定を250から500へ書き換える。

リネーム&バックアップ
% mv photos-src_views_Albums_vue.js photos-src_views_Albums_vue.js.org
sedで置き換えながら複製
% sed -e 's/x=",250,"&y=",250,/x=",500,"\&y=",500,/'  photos-src_views_Albums_vue.js.org >  photos-src_views_Albums_vue.js
構成ファイルの中を書き換えると、Nextcloudの管理画面 > 概要で構成ファイルのハッシュが合わないというアラートが設定画面に出るが無視。

Mapsアプリの改修

地図表示をするMapsも同様の問題を抱えているのでプレビュー解像度を256 x 256 から500 x 500ファイルを使い回すように改修する。

apps/maps/js/script.js をsedで中を書き換える。
% cp script.js script.js.org
% sed -e 's/x=341&y=256/x=500\&y=500/g' script.js.org > script.js

アプリが扱うプレビュー解像度がバラバラすぎる問題

Nextcloudで大量のプレビュー画像が生成されてしまう原因は、プラグインとして入れるアプリ機能がそれぞれ好き勝手な解像度のプレビュー画像を生成させることである。Nextcloudは公式採用するアプリにはプレビュー画像の解像度のガイドラインを設けるべきだと思う。

現在はだいぶ改善したが、Nextcloudはv22まで大量に生成されるプレビュー画像の保存ディレクトリの管理にも問題があった。それは大量のプレビュー画像を一つのディレクトリに保存してしまうことであった。ディレクト内のファイルは多くても1,000個以下に抑えた方が良い。ディレクトリ内に大量のファイルが格納されていると、パフォーマンスで問題が生じる。
v23からサブディレクトリを用いた管理へ改善しているが、プレビュー画像の解像度バリエーションの問題がまだ根強く残っている面を見ると、大量ファイルを扱うクラウドサービスの構築の際に必要な配慮が少々足りてないなと思うところがある。

そのようなことがあるのでここに書いた対応をした。結果、プレビュー画像のバリエーションは2種類まで減らすよう改善ができ、動作はかなりサクサクにさせることはできた。

Profile

Profile header image


ampspeed

炎上している開発プロジェクトの火消し屋をやってます。

サイト内検索