TinyPNG API を使って、画像を最適化してみよう

この記事は Advent Calendar 2019 の25日目の記事となります。さまざまな a-blog cms に関する記事が公開されていますので是非ごらんください。



画像を最適化してファイルサイズを小さくすることでパフォーマンスをよくすることができます。
これを実現するために「ロスレス圧縮」機能が a-blog cms にはありますが、サーバー側で必要なライブラリが入っていないと動作しません。

必要なライブラリ例

  • pngquant
  • optipng
  • pngcrush
  • pngout
  • advpng
  • jpegtran
  • jpegoptim
  • gifsicle
  • svgo

共用のレンタルサーバーなどだと、これらのライブラリが入っている可能性が少なく、対応に困ります。
そこで、画像最適をするサービスで有名な「TinyPNG」のAPIを使い、ライブラリが入っていないサーバーでも画像のロスレス圧縮に対応させる拡張アプリを作ってみたので紹介いたします。

この拡張アプリで実現できること

TinyPNGのAPIを利用して、画像のロスレス圧縮を実現できます。Hook機能を使って画像生成時にすべてロスレス圧縮します。 また校正オプションを用意していて、ロスレス圧縮された状態で画像のリサイズをすることができます。

インストール方法

設置方法

https://github.com/appleple/acms-tiny-png/raw/master/build/TinyPNG.zip のリンクからzipファイルをダウンロードします。zipを解凍すると TinyPNG というディレクトリが出てくるので extension/plugins/ に設置します。

アプリのインストール

設置できたら、管理画面にログインし、拡張アプリ から TinyPNG 拡張アプリをインストールします。

config.server.php

拡張アプリのインストールができたら、config.server.php の HOOK_ENABLE の値を「1」に設定します。

APIキーの設定

https://tinypng.com/ でアカウントを作成し、API Key を発行ください。発行した API Key を以下の様に private/config.system.yaml に追記します。

tiny_png_api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxx

以上でインストール・設定は完了です。

参照: https://github.com/appleple/acms-tiny-png

使い方

特にロスレス圧縮するだけなら、なにも意識せず通常通りの作業で、自動的にTinyPNGのAPIを通して、圧縮されるようになります。

画像のリサイズを TinyPNG APIで実行する

TinyPNGで画像を好きなサイズにリサイズできる校正オプションも用意しました。もちろんロスレス圧縮された状態でリサイズされます。

scale

アスペクト比を維持したまま、幅または高さを指定してリサイズします。幅、高さの両方の指定はできません。

<img src="%{MEDIA_ARCHIVES_DIR}{hoge@path}[resizeTinyPng('scale', 300)]" /> <!-- 幅指定 -->
<img src="%{MEDIA_ARCHIVES_DIR}{hoge@path}[resizeTinyPng('scale', 0, 300)]" /> <!-- 高さ指定 -->

fit

アスペクト比を維持したまま、指定された寸法に収まるように縮小します。幅と高さ両方の指定が必要です。

<img src="%{MEDIA_ARCHIVES_DIR}{hoge@path}[resizeTinyPng('fit', 300, 300)]" />

cover

指定された寸法になるように縮小し、はみ出した部分はトリミングします。幅と高さ両方の指定が必要です。

<img src="%{MEDIA_ARCHIVES_DIR}{hoge@path}[resizeTinyPng('cover', 200, 200)]" />

改善したいところ

さくっと作ったままですので、まだまだ改善しないといけないところがあります。

  • 同期的にAPIをたたいているので、画像のアップロード処理がすごく遅くなってしまう。-> バックグラウンドで実行する様に修正する。
  • 余分なAPIコールがあるので、節約するようにしたい。
  • 残りAPI回数が、管理画面から確認できるようにしたい。

まだまだ改善点はありますが、この拡張アプリを入れることで、共用のレンタルサーバーなどでも簡単にロスレス圧縮に対応できるようになります。 よかったらぜひ試してみてください。

Custom Search API との連携

JSON形式のデータを表示させることができるビルトインモジュール Json_2Tpl とGoogleの検索結果をJSON形式で取得することができるCustom Search API を連携させて Google の検索結果を使用するサイト内検索を実装することができます。

a-blog cms標準のサイト内検索機能はエントリー単位の検索ですが、 Custom Search API を連携させることで、一覧ページやトップページなどのページも検索対象に含めることができます。また、検索結果自動的に関連度順に表示されるため、普段Googleで検索するときのような検索体験を実現することができます。

Googleのサイト内検索を実装するためには以下の情報が必要になります。

  • GoogleのAPIキー
  • Googleカスタム検索エンジンの検索エンジンID

準備1: APIキーを用意する

Google Cloud Platform の管理画面からAPIキーを作成してください。 https://console.cloud.google.com/

必要であればIP アドレス(ウェブサーバー、cron ジョブなど)でAPIキーを使用できるウェブサイト、IP アドレス、アプリケーションを制御してください。

※Json_2Tpl ではwebサーバー側でHTTPリクエストを送信するため、HTTPリファラー(URL)による制限はできません。


HTTPリファラー(ウェブサイト)や、IP アドレス(ウェブサーバー、cron ジョブなど)でAPIキーを使用できるウェブサイト、IP アドレス、アプリケーションの制御を設定する画面

また、APIの制限も行いましょう。


APIの制限をする

準備2: 検索エンジンの設定

Google カスタム検索エンジンにログインして、検索エンジンの作成と検索エンジンIDの取得をします。
https://programmablesearchengine.google.com/

1. 検索エンジンの作成

検索対象のドメインと任意の名前を設定して検索エンジンを作成します。
注意点として検索できるサイトでないといけないので、ablogcms.ioなどではなく、ご自身のサイトなどを設定ください。


検索エンジンの作成

2. 検索エンジンIDの取得

コントロールパネルの「基本」タブ内、検索エンジン IDをコピーします。


検索エンジンIDの取得

サイト内検索の実装

APIキーと検索エンジンIDがそろったらa-blog cmsのカスタマイズを行い、Googleの検索結果を使用したサイト内検索を実装します。

1. モジュールIDの作成・設定

Json_2Tpl モジュールのモジュールIDを作成し、表示設定のJSONファイルに以下のURLを設定します。

https://www.googleapis.com/customsearch/v1?key=認証キー&cx=検索エンジンID&q=%{KEYWORD}&hl=ja&start=%{start}

&start=%{start} の部分はページャー機能を実装する場合に必要になります。



2. テンプレートの記述

Json_2Tplのテンプレートを書くためには、jsonの中身を確認する必要がありますが、例えばキーワードを "api" としたとき、検索結果のデータを1件目から取得したい場合は下記のURLをブラウザのアドレスバーに入力して検索すると、jsonの中身を確認することができます。 https://www.googleapis.com/customsearch/v1?key=認証キー&cx=検索エンジンID&q=api&hl=ja&start=1

jsonデータの中身が確認できたら、Json_2Tpl の テンプレートの書き方 を参考に検索結果を表示するテンプレートを記述します。

<!-- BEGIN_MODULE Json_2Tpl id="google_custom_search" -->
<div class="google-search clearfix">
	<!-- BEGIN items:loop -->
	<div class="search-item clearfix">
		<h2>
			<a href="{link}">{htmlTitle}[raw]</a>
		</h2>
		<p class="link">{link}[trim(100, '...')]</p>
		<div class="acms-col-md-10">
			<p>{snippet}</p>
		</div>
		<div class="acms-col-md-2 acms-sp-hide">
			<!-- BEGIN pagemap -->
			<!-- BEGEN metatags:loop -->
			<img src="{og:image}[resizeImg(130)]" width="130">
			<!-- END metatags:loop -->
			<!-- END pagemap -->
		</div>
	</div>
	<!-- END items:loop -->

  <ul class="search-pager">
		<!-- BEGIN queries -->
		<!-- BEGIN previousPage:loop -->
		<li class="link-item">
			<a href="search.html?keyword=%{KEYWORD}&start={startIndex}" class="btn">前</a>
		</li>
		<!-- END previousPage:loop -->

		<!-- BEGIN nextPage:loop -->
		<li class="link-item">
			<a href="search.html?keyword=%{KEYWORD}&start={startIndex}" class="btn">次</a>
		</li>
		<!-- END nextPage:loop -->
		<!-- END queries -->
  </ul>
</div>
<!-- END_MODULE Json_2Tpl -->

ページャーは、a-blog cms標準機能であるクエリストリングの値をグローバル変数として取得するを利用して実装しています。
具体的には、{startIndex} の値をクエリストリングに渡すことで、Json_2Tpl モジュールの表示設定、JSONファイルで設定したURLの %{start}のグローバル変数が {startIndex}を参照するようになります。

次にキーワード検索をするためのフォームを設置します。

<form action="/search.html" class="search-form" method="post" role="search" aria-label="検索フォーム">
	<input type="hidden" name="tpl" value="/search.html">
	<input type="hidden" name="query[]" value="keyword">
	<input type="hidden" name="query[]" value="start">
	<input type="hidden" name="bid" value="%{BID}">
        <input type="hidden" name="start" value="1">
	<input type="text" name="keyword" class="search-form-text" value="%{KEYWORD}" size="15" placeholder="検索キーワード">
	<span class="search-form-btn-wrap">
		<button type="submit" name="ACMS_POST_2GET" class="search-form-btn"><span class="acms-icon-search"></span>
		</button>
	</span>
</form>

完成


完成形

検索フォームに適当な検索ワードを入力し、検索ボタンをクリックした時に、画像のような検索画面が表示されていれば完成です。スタイルやHTMLの構造を変えることで、オリジナルのレイアウトにすることも可能です。

追記:複数ワード対応

上記の実装で検索はヒットするようになりましたが、複数ワードをスペースで区切って検索すると検索結果が出てきません。

方法としては、半角スペース&全角スペースを半角カンマに置き換えたグローバル変数 %{API_KEYWORD} を作成し、利用する事で複数ワードでの検索に対応できるようになります。

config.server.php

define('HOOK_ENABLE', 1);

extension/acms/Hook.php

    /**
     * グローバル変数の拡張
     *
     * @param array $globalVars
     */
    public function extendsGlobalVars(&$globalVars)
    {
        // $globalVars->set('key', 'var');
        $globalVars->set('API_KEYWORD', str_replace(' ', ',', mb_convert_kana(KEYWORD,'s','utf-8')));

    }
  1. モジュールIDの作成・設定 の Json_2Tpl の設定画面で %{KEYWORD} と設定していた部分を %{API_KEYWORD} と修正ください。