カテゴリー: テクノロジー

画像最適化、してますか?


こんにちは。デザイナーのMです。

突然ですが、皆さんは画像最適化してますか?(唐突
私はしています。

弊社ではサイトの制作などをさせて頂く機会が多々ありまして、制作の最終段階であるサーバーアップの前に、サイト内で使用する画像を圧縮して最適化するというフローがあります。

Webサイトのスマホ閲覧の割合がPCと半々、場合によってはPCの閲覧比率を超えることも珍しくなくなってきたので、通信容量(いわゆる”ギガ”)を少なくして、サイトを高速に表示させることが必須となっています📱

そこで今回は、普段使用している画像最適化ツールを紹介したいと思います。

TinyPng


https://tinypng.com/

「パンダのやつ」と僕は呼んでいます🐼
けっこう昔からあるので、かなり有名なオンライン画像圧縮ツールだと思います。
サイトを見て頂く通り、パンダが画像を圧縮してくれます。
Chromeのブックマークバーに突っ込んでるので、ブラウザ上からサクッと圧縮できる手軽さが便利です。
一気に20枚、または上限5MBまでの画像ファイル(jpg/png)を圧縮可能です。
gifよりきれいなアニメーションができる、Animated PNG(APNG)にも対応しています。
ページの下の方でパンダがぬるぬる手を振っていますね👋

対応ファイル形式:jpg / png / APNG

jpeg.io


https://www.jpeg.io/

こちらのサービスは、単純に画像を圧縮するというわけではないのですが、様々な形式の拡張子のファイルを一括でjpgに変換して最適化してくれるツールです。
png形式の画像はjpg形式に比べてファイルサイズが大きくなることがあるので、こちらを利用してjpgに変換することにより容量の削減になることもあります。特に写真を利用している素材とかですね📷
他にもpsdでワイヤーやカンプを作成して画像として書き出す際にも、こちらを使えばpsdからそのままjpgを作成してくれるので便利です。

対応ファイル形式:jpg / png / gif / svg / bmp / eps / psd / tiff / WebP

Squoosh


https://squoosh.app/

Googleが開発したオンライン画像圧縮ツールです。
サイトにアクセスして、画像をドラッグ&ドロップするとプレビュー編集画面に遷移します。
この画面上で画質や圧縮形式などを設定することができます。
また、画面の左右で圧縮前と圧縮後のプレビューを見比べることもできるので保存後のイメージを目で確認しながら調整することができます。めっちゃ便利👀
ただ、現状では画像1枚ずつしか編集することができないので、一括で圧縮をしたい場合には向いていません🙅‍♂️
私の場合は、画質を極力落とさずに綺麗な見た目のまま写真の容量を圧縮したいときなどのシビアな圧縮条件が求められる場合に利用することが多いです(あまりないですがw
ちなみにリサイズも同時にできるので、さらに容量圧縮したい場合などにも使えます。

対応ファイル形式 : jpg / JPEG2000 / mozjpeg / png / gif / pdf / bmp / tiff / WebP

ImageOptim(Mac向けアプリ)


https://imageoptim.com/

ImageOptimは、Mac向けの画像圧縮専用アプリです。
ブラウザからいつでも圧縮はできませんが、いつも利用しているPCにインストールしておけばすぐに使うことができます。
私はいつもこのアプリを使って画像を最適化することが多いです。
アプリをインストールして、Finderで画像を右クリックすると「ImageOptimize」という項目が表示されます。
その項目をクリックすると自動でアプリが起動して、画像最適化を開始してくれます。便利!
容量制限なしで、複数選択もできるのでバシバシ使用することができます🤘

ファイル形式:jpg / png / gif

ReSizer(iOSアプリ)


https://apps.apple.com/jp/app/jp/id1388705112

弊社が開発したiOS向けの画像リサイズアプリです。
直感的なUIで画像を好きなサイズにリサイズ、画質を選択することで容量も最適化することができます。
カメラロールからの一括選択にも対応。画像に関するExif情報の確認や削除もこのアプリからできます。
スマホからブログを書く際や、ストレージの容量を確保したいときなどに大変重宝するアプリなのでぜひ一度インストールしてみてください✨



ぜひ今回ご紹介したツールを利用して画像の最適化をしてみましょう!
楽しく快適なインターネットライフを🥺

Solrのコレクション名に日本語を使えるか

先日、テーブル名やカラム名に日本語が使われているデータベースからデータをSolrにインポートする機会がありました。Solr側のコレクション名やフィールド名で日本語を使えるならインポートの手間が小さくて済むので、実際そういうことができるのか調べてみました。

コレクションAPIで日本語名のコレクションを作ってみます。

$ curl -s 'http://localhost:8983/solr/admin/configs?action=CREATE&omitHeader=true&name=test1&baseConfigSet=_default'
$ curl -s 'http://localhost:8983/solr/admin/collections?action=CREATE&name=テスト&numShards=1&replicationFactor=1&wt=json'
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Error 500 Server Error</title>
</head>
<body><h2>HTTP ERROR 500</h2>
<p>Problem accessing /solr/admin/collections. Reason:
<pre>    Server Error</pre></p><h3>Caused by:</h3><pre>org.apache.solr.common.SolrException: URLDecoder: The query string contains a not-%-escaped byte > 127 at position 19
(略)

サーバエラーになってしまったのでURLエンコードしてパラメータ指定します。

$ curl -s 'http://localhost:8983/solr/admin/collections?action=CREATE&name=%E3%83%86%E3%82%B9%E3%83%88&numShards=1&replicationFactor=1'
{
  "responseHeader":{
      "status":400,
      "QTime":0},
      "error":{
      "metadata":[
        "error-class","org.apache.solr.common.SolrException",
        "root-error-class","org.apache.solr.common.SolrException"],
        "msg":"Invalid collection: [テスト]. collection names must consist entirely of periods, underscores, hyphens, and alphanumerics as well not start with a hyphen",
        "code":400}}
エラーメッセージによると、コレクション名に利用できるのは以下の文字種に限るようです。
  • 英数文字
  • ピリオド
  • アンダースコア
  • ハイフン(ハイフンは先頭文字としては使えない)

ソースコードを調べると、コレクション名のチェックをしているのは以下のクラスでした。

solr/solrj/src/java/org/apache/solr/client/solrj/util/SolrIdentifierValidator.java

チェックに使う正規表現は以下の通りです。

final static Pattern identifierPattern = Pattern.compile("^(?!\\-)[\\._A-Za-z0-9\\-]+$");

SolrIdentifierValidatorではシャード、コレクション、コア、エイリアスの名前をチェックしています。チェックに使う正規表現は共通なので、シャード、コレクション、コア、エイリアスについては同じ仕様であることが分かりました。

Solrのフィールド名に日本語を使えるか

前回の記事に引き続き、今回はフィールド名に日本語を使えるかどうかを調査しました。

フィールド名の仕様については、Solrリファレンスガイドの”Field Type Definitions and Properties”に記載があります。

The name of the fieldType. This value gets used in field definitions, in the “type” attribute. It is strongly recommended that names consist of alphanumeric or underscore characters only and not start with a digit. This is not currently strictly enforced.

  • 英数字(先頭文字として数字は使えない)
  • アンダースコア

「今は厳密には強制していません」というのが微妙なところです。“Defining Fields”にもう少し詳しい説明がありました。

Field names should consist of alphanumeric or underscore characters only and not start with a digit. This is not currently strictly enforced, but other field names will not have first class support from all components and back compatibility is not guaranteed.

英数字とアンダースコア以外の文字も使えないことはないけど、全部のコンポーネントがサポートしているとは限らないよ、ということのようです。

フィールド名に日本語を使うとどういうことが起こるか試してみました。

まずtestという名のコレクションを作成します。

$ curl -s 'http://localhost:8983/solr/admin/collections?action=CREATE&name=test&numShards=1&replicationFactor=1'

「日付」というフィールドを追加します。

$ cat add_field_j.json 
{
  "add-field":{
     "name":"日付",
     "type":"pdate",
     "stored":true }
}
$ curl -s -X POST -H 'Content-type:application/json' -d @add_field_j.json http://localhost:8983/solr/test/schema

APIで「日付」フィールドが存在することを確認できます。

$ curl -s 'http://localhost:8983/solr/test/schema/fields/%E6%97%A5%E4%BB%98'
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "field":{
    "name":"日付",
    "type":"pdate",
    "stored":true}}

「日付」フィールドに値を持つデータを投入してみます。

$ cat j.json
{"日付":"2019-01-01"}
$ ./post -c test j.json

比較のため、”date”フィールドを追加してデータを投入します。

$ cat add_field_e.json
{
  "add-field":{
     "name":"date",
     "type":"pdate",
     "stored":true }
}
$ curl -s -X POST -H 'Content-type:application/json' -d @add_field_e.json http://localhost:8983/solr/test/schema
$ cat e.json
{"date":"1999-01-01"}
$ ./post -c test e.json

データがどう保持されているか比較します。

$ curl -s 'http://localhost:8983/solr/test/select?q=*%3A*&omitHeader=true'
{
  "response":{"numFound":2,"start":0,"docs":[
      {
        "id":"578b7513-831e-4ef3-bdb9-770268f27a7e",
        "__":["2019-01-01T00:00:00Z"],
        "_version_":1651454167605051392},
      {
        "date":"1999-01-01T00:00:00Z",
        "id":"892eb3b5-a648-45a5-80c1-5b975e8ebc47",
        "_version_":1651454169922404352}]
  }}

“date”の方は特に問題ありません。「日付」フィールドは無く”__”というフィールドに値が格納されています。値が配列になっていることから、「日付」フィールドの定義とは異なるフィールドとして扱われていることが分かります。何か別のダイナミックフィールドのルールにヒットしたようです。

当然検索もできません。

$ curl -s 'http://localhost:8983/solr/test/select?q=%E6%97%A5%E4%BB%98%3A%5B*%20TO%20*%5D&omitHeader=true'
{
  "response":{"numFound":0,"start":0,"docs":[]
  }}
$ curl -s 'http://localhost:8983/solr/test/select?q=date%3A%5B*%20TO%20*%5D&omitHeader=true'
{
  "response":{"numFound":1,"start":0,"docs":[
      {
        "date":"1999-01-01T00:00:00Z",
        "id":"892eb3b5-a648-45a5-80c1-5b975e8ebc47",
        "_version_":1651454169922404352}]
  }}

というわけで、割と基本的なところで躓いてしまいました。結論としては、フィールド名に日本語を使うのも実用としては無理ということになりそうです。

面倒な事をシステムでなんとかする

弊社では勤怠システムがWEBシステムになっているのですが、忘れることがあったのでえいやーでなんとかしてみました。

・WEB上に勤怠システムがある
・emailアドレスとパスワードでログインする必要がある
・システムではlaravelのtoken処理が使われているらしく、cookieとtokenも一致させなければいけない
・Macの起動と終了で勤務時間とみなす
・自分しか使わない前提でとりあえず動くもの

ひとまず出勤と退勤処理をするためのプログラムを作成
できあがったものが以下

#!/usr/bin/env bash

function usage {
  cat <<EOM
Usage: $(basename "$0") [OPTION]...
  -h Display help
  -e VALUE E-mail address
  -p VALUE Password
  -t VALUE Type(start, end)
EOM

  exit 2
}

API_HOST='kintai-system-no-host-dayo'
_EMAIL=''
_PASS=''
_TYPE='start'

while getopts ":e:p:t:h" optKey; do
  case "$optKey" in
    e)
      _EMAIL="${OPTARG}"
      ;;
    p)
      _PASS="${OPTARG}"
      ;;
    t)
      if [ "x${OPTARG}" = "xstart" ]; then
        _TYPE='start'
      elif [ "x${OPTARG}" = "xend" ]; then
        _TYPE='end'
      else
        echo "-t ${OPTARG}: unknown type."
        usage
      fi
      ;;
    '-h'|'--help'|* )
      usage
      ;;
  esac
done


if [ "x${_EMAIL}" = "x" ]; then
  read -p "E-mail address: " _EMAIL
fi

if [ "x${_PASS}" = "x" ]; then
  read -sp "Password: " _PASS
fi

# cookieの保存場所
tmpfile=$(mktemp)

# トークンの取得
_TOKEN=$(curl -s -c ${tmpfile} https://${API_HOST}/login | xmllint --html --xpath '//*form/input/@value' - | grep value= | sed 's/[^"]*"\([^"]*\)"[^"]*/\1/g')

# ログイン
curl -L -b ${tmpfile} -c ${tmpfile} -d "_token=${_TOKEN}&email=${_EMAIL}&password=${_PASS}" https://${API_HOST}/login

# 出勤もしくは退勤
curl -L -b ${tmpfile} -c ${tmpfile} -d "_token=${_TOKEN}" https://${API_HOST}/dashboard/${_TYPE}/

exit

適当なファイル名eiya-de-kintai.shとして保存し、実行権限をつけておきます。

$ chmod +x ~/eiya-de-kintai.sh



実行するには以下のようにします。
出勤

$ ~/eiya-de-kintai.sh -e email@example.splout.co.jp -p password-desu -t start


退勤

$ ~/eiya-de-kintai.sh -e email@example.splout.co.jp -p password-desu -t end

プログラムの内容は
・curlでログインページの取得を行い、一時ファイルにcookieを保存
 同時にレスポンスbodyからxpathでinput要素のvalueにあるtokenを取得
・取得したcookieとtokenとメールアドレス(-e)とパスワード(-p)をログインページへPOSTし、ログイン状態のcookieを保存
・出勤もしくは退勤(-tで指定)のエンドポイントへログイン状態のcookieを使いtokenをPOST

ログイン判定してないとかcookieが残骸として残る等かなり手を抜いているがひとまず気にしない。徐々に手を加えていく予定。

Macの起動と終了で処理を走らせるには、「Macの起動=アプリケーションの起動」、「終了=アプリケーションの終了」とみなすことにし、作成したシェルスクリプトを呼び出すApplescriptを作成しました。

Macに標準で入っているスクリプトエディタを起動

↑こんなアイコン

以下のコードを記述

on run
	do shell script "~/eiya-de-kintai.sh -e email@example.splout.co.jp -p password-desu -t start"
end run

on quit
	do shell script "~/eiya-de-kintai.sh -e email@example.splout.co.jp -p password-desu -t end"
	continue quit
end quit

これを
 ファイルフォーマット:アプリケーション
 オプション:ハンドラの実行後に終了しない。
で保存します。

出来上がったアプリケーションをMacのログイン時に実行されるようにします。
「システム環境設定」にある「ユーザとグループ」を開き
「ログイン項目」へ作成したアプリケーションの登録を行います。

以上でとりあえずのものが出来上がりました。

Macを起動し、ログインすると自動で出勤処理が行われます。
Macの終了時には自動で退勤処理が行われ、何も意識しなくても勤怠処理が行われるようになりました。


そう、エラーさえなければ・・・

アカウント情報を平文で書いているとかApplescript使うならシェルいらないのでは?とかエラー処理されていないとか、実用するにはまだまだまだまだ改善すべき箇所が多く残っていますが、ひとまず目標は達成しました。

SolrのReadOnlyモード

Solr 8.1から追加された機能にRead-Onlyモードがあります。SolrCloudでの運用時に利用できるモードです。リファレンスマニュアルの該当部分を翻訳してみました。

readOnly属性をtrueにセットするとコレクションはRead-Onlyモードに移行します。このモードではインデックスの更新リクエストは拒否されます。他のコレクションレベルのアクション(コレクションの追加/削除、レプリカの移動)はこのモードにおいても有効です。


(デフォルトの)read-writeモードからread-onlyモードへは以下の手順で移行されます。
・コレクション内のreadOnlyフラグが変更される
・すべての更新リクエストが403 FORBIDDENエラーで拒否される(継続中の長期実行リクエストは中止される)
・実行中の更新が強制的にコミットされる
(注意)大きなセグメントがバックグラウンドでマージされている最中であれば、この強制コミットは長時間に及ぶかもしれません。
・コレクションのRELOADアクションが実行される
readOnly属性が削除されるかfalseに設定されるかすると、更新リクエストが有効になりコレクションがリロードされます。

https://lucene.apache.org/solr/guide/8_2/collection-management.html#readonlymode

簡単な動作確認をしてみました。

$ curl 'http://localhost:8983/solr/admin/collections?action=CREATE&name=test_readonly&collection.configName=_default&numShards=1&wt=xml'

$ curl 'http://localhost:8983/solr/admin/collections?action=MODIFYCOLLECTION&collection=test_readonly&readOnly=true'
{
  "responseHeader":{
    "status":0,
    "QTime":476},
  "success":{
    "127.0.1.1:8983_solr":{
      "responseHeader":{
        "status":0,
        "QTime":350}}}}
$ bin/post -c test_readonly data.json 
SimplePostTool version 5.0.0
Posting files to [base] url http://localhost:8983/solr/test_readonly/update...
Entering auto mode. File endings considered are xml,json,jsonl,csv,pdf,doc,docx,ppt,pptx,xls,xlsx,odt,odp,ods,ott,otp,ots,rtf,htm,html,txt,log
POSTing file data.json (application/json) to [base]/json/docs
SimplePostTool: WARNING: Solr returned an error #403 (Forbidden) for url: http://localhost:8983/solr/test_readonly/update/json/docs
SimplePostTool: WARNING: Response: {
  "responseHeader":{
    "status":403,
    "QTime":207},
  "error":{
    "metadata":[
      "error-class","org.apache.solr.common.SolrException",
      "root-error-class","org.apache.solr.common.SolrException"],
    "msg":"Collection test_readonly is read-only.",
    "code":403}}
SimplePostTool: FATAL: Looks like Solr is secured and would not let us in. Try with another user in '-u' parameter

テスト用のコレクション(test_readonly)を作成し、MODIFYCOLLECTIONアクションでreadOnlyをtrueに設定してからドキュメントをポストするとリファレンスの説明通り403エラーになりました。

$ curl 'http://localhost:8983/solr/test_readonly/select?q=*%3A*&rows=1&start=0'
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":1,
    "params":{
      "q":"*:*",
      "start":"0",
      "rows":"1"}},
  "response":{"numFound":0,"start":0,"docs":[]
  }}

当然インデックスは空っぽのままです。

$ curl 'http://localhost:8983/solr/admin/collections?action=MODIFYCOLLECTION&collection=test_readonly&readOnly=false'
{
  "responseHeader":{
    "status":0,
    "QTime":466},
  "success":{
    "127.0.1.1:8983_solr":{
      "responseHeader":{
        "status":0,
        "QTime":344}}}}
	
$ bin/post -c test_readonly data.json 
SimplePostTool version 5.0.0
Posting files to [base] url http://localhost:8983/solr/test_readonly/update...
Entering auto mode. File endings considered are xml,json,jsonl,csv,pdf,doc,docx,ppt,pptx,xls,xlsx,odt,odp,ods,ott,otp,ots,rtf,htm,html,txt,log
POSTing file data.json (application/json) to [base]/json/docs
1 files indexed.
COMMITting Solr index changes to http://localhost:8983/solr/test_readonly/update...
Time spent: 0:00:01.420

次にMODIFYCOLLECTIONアクションでreadOnlyをfalseに戻してからドキュメントをポストしたところ、今度はエラー無しでアップデートできました。

$ curl 'http://localhost:8983/solr/test_readonly/select?q=*%3A*&rows=1&start=0'
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":0,
    "params":{
      "q":"*:*",
      "start":"0",
      "rows":"1"}},
  "response":{"numFound":9236,"start":0,"docs":[
      {
        "id":"158",
        "type":["官公庁"],
        "area":["住之江区"],
        "name":["軽自動車検査協会大阪主管事務所"],
        "address":["住之江区南港東3-4-62"],
        "_version_":1648645250184380416}]
  }}

クエリの結果からも、正しく更新できたことを確認できます。

ReadOnlyモードを使えば内容固定のインデックスを運用中に誤って更新してしまうことを防ぐことができて便利ですが、このモードはSolrCloudでのみ利用可能なので、その点は注意が必要です。