Milk+ea

Weblog Is My Hobby.

TinyPNGのAPIを使って画像圧縮をコマンドラインからやってみる

TinyPNGのAPIを使って画像圧縮をコマンドラインからやってみる

画像圧縮サービスは色々ありますが、その中でも良く使うお気に入りはTinyPNGです。
PNGやJPGのドラッグアンドドロップだけで、それらの圧縮画像がダウンロードできてしまうこのサイト、 実はAPIも提供されていて、誰でも1ヶ月あたり500枚までなら無料で利用することができます。
正直個人で1ヶ月500枚もできれば十分だし、cliで使ってみて便利だったらいいなってことで使ってみました。

事前準備

APIを使うためにまず、APIキーを取得しないといけません。が、この作業は2分ほどで終わります。

  1. 登録ページへアクセスし、フォームに名前とメールアドレスを入力し登録 f:id:totora0155:20151025074423p:plain
  2. 入力したメールアドレスにメールが届くので、そこに載ってるリンクへアクセス f:id:totora0155:20151025074519p:plain
  3. APIキーゲットだぜ f:id:totora0155:20151025074523p:plain

画像のアップロードとダウンロード

APIの流れとして、ダウンロードの前にまずはファイルのアップロードが必要です。試しのこの画像を使ってやってみます。f:id:totora0155:20151025173506p:plain

画像のアップロードは次のようなコマンドです。--data-binary @の後に、アップロードするカレントディレクトリ内の適当な画像ファイル名を書きます。

curl https://api.tinify.com/shrink \
--user api:[自分のAPIキーをここへ] \
--dump-header /dev/stdout \
--data-binary @sample.png

### レスポンスヘッダー
# HTTP/1.1 100 Continue
#
# HTTP/1.1 201 Created
# Cache-Control: no-cache
# Compression-Count: 40
# Content-Type: application/json; charset=utf-8
# Date: Sat, 24 Oct 2015 20:22:39 GMT
# ETag: W/"b8-84xphfraWor8XlpyrQ9bIw"
# Location: https://api.tinify.com/output/pok3j64g1ielhm12.png
# Server: Apache/2
# Strict-Transport-Security: max-age=31536000
# X-Powered-By: Voormedia (voormedia.com/jobs)
# Content-Length: 184
# Connection: keep-alive

### レスポンステキスト
# {
#   "input":{
#     "size":44464,
#     "type":"image/png"},
#     "output":{
#       "size":11607,
#       "type":"image/png",
#       "width":800,
#       "height":567,
#       "ratio":0.261,
#       "url":"https://api.tinify.com/output/pok3j64g1ielhm12.png"
#     }
#   } 

色々表示されますが、ヘッダーに含まれるLocation: https://api.tinify.com/output/pok3j64g1ielhm12.pngというURLが、 実際のダウンロードに必要なURLです。*1
それをコピーし、次のようなのコマンドで画像をダウンロードします。

curl -s https://api.tinify.com/output/pok3j64g1ielhm12.png \
--user api:[自分のAPIキーをここへ] \
--dump-header /dev/stdout \
--output min.png

### レスポンスヘッダー
# HTTP/1.1 200 OK
# Cache-Control: max-age=604800
# Compression-Count: 40
# Content-Disposition: attachment
# Content-Type: image/png
# Date: Sat, 24 Oct 2015 20:36:33 GMT
# ETag: W/"2d57-bBg1XoGYF5t8uVDe3d208A"
# Expires: Sat, 31 Oct 2015 20:36:33 GMT
# Image-Height: 567
# Image-Width: 800
# Server: Apache/2
# Strict-Transport-Security: max-age=31536000
# X-Powered-By: Voormedia (voormedia.com/jobs)
# Content-Length: 11607
# Connection: keep-alive

--outputの値には自分の好きなファイル名を入力します。
終了後、カレントディレクトリにmin.pngが作られてれば完了です!

楽にコマンドを実行できるようにする

長いコマンドなので(特にapiとか覚えられないので)、functionを作って上記のコマンドを実行できるようにしてみます。

function tinify() {
  [[ $1 = '' ]] || [[ $2 = '' ]] && echo 'Usage: tinify [input] [output]' && return 1

  local input=$1
  local output=$2
  local api=[自分のAPIキーをここへ] 
  local uploaded=`curl -s https://api.tinify.com/shrink \
                   --user api:$api \
                   --dump-header /dev/stdout \
                   --data-binary @$input \
                   | grep Location | cut -d' ' -f2 | tr -d '\r\n'`
  curl -s $uploaded --user api:$api --output $output
}

上のコードを.bashrc.zshrcなどに書けば、tinify sample.png sample-min.pngだけで、 圧縮画像を作れます!

### 実行前のディレクトリの様子
tree
# .
# └── sample.png
# 
# 0 directories, 1 file

### sample.pngを圧縮して、カレントディレクトリにsample-min.pngを作る
tinify sample.png sample-min.png

### 実行後のディレクトリの様子
# tree
# .
# ├── sample-min.png
# └── sample.png
#
# 0 directories, 2 files

### ファイルサイズ確認 43K -> 11K
ls -lh | awk '(NR > 1){print $5 "\tfilename: " $9}' 
# 11K  filename: sample-min.png
# 43K  filename: sample.png

圧縮と同時にリサイズも行う

APIを使うとなんと、画像をリサイズしたものをダウンロードすることもできます。
先ほどのダウンロード用のコードに少し情報を足してみます。

curl -s https://api.tinify.com/output/pok3j64g1ielhm12.png \
--user api:[自分のAPIキーをここへ] \
--dump-header /dev/stdout \
--header 'Content-Type: application/json' \
--data '{
  "resize": {
    "method": "scale",
    "width": 500
  }
}' \
--output scale-min.png

リサイズメソッドscaleにすると、width(またはheight)のサイズに比率を維持したままリサイズしてくれます。

リサイズメソッドには他に、 fit*2cover*3というのもあります。が、それほど使わないと思ったので今回は略します。

Funcitonをリサイズ用に書き換える

先ほどのfuncitonをtinify input.png output.png 500 heightみたくサイズを指定できるように変更してみました。*4

function tinify() {
  [[ -z $1 ]] || [[ -z $2 ]] && echo 'Usage: tinify [input] [output] *[size] *[width or height]' && return 1

  local input=$1
  local output=$2
  local size=$3
  local tax=`[[ -z $4 ]] && echo width || echo $4`
  local api=nFZdkWRFXCKg0wNslk1xPy-JpGQytE_Y
  local uploaded=`curl -s https://api.tinify.com/shrink \
                   --user api:$api \
                   --dump-header /dev/stdout \
                   --data-binary @$input \
                   | grep Location | cut -d' ' -f2 | tr -d '\r\n'`

  if [[ -z $size ]]; then
    curl -s $uploaded --user api:$api --output $output
  else
    # widthかheight以外なら終了
    [[ $tax -eq 'width' ]] || [[ $tax -eq 'height' ]] || return 1


    local json=`[[ $tax = 'width' ]] \
                && echo "{\"resize\":{\"method\":\"scale\",\"width\":$size}}" \
                || echo "{\"resize\":{\"method\":\"scale\",\"height\":$size}}"`
    curl -s $uploaded --user api:$api --output $output \
    --header 'Content-Type: application/json' \
    --data $json
  fi

  # 最後に出力ファイル名を表示
  echo 'Created' $output
}

ついでに、パイプで繋げれるようにechoしてます。
これで次の様に使えるようになりました!

# 圧縮したものを同じディレクトリへ
tinify sample.png sample-min.png
# 圧縮したものをmin/ディレクトリへ
tinify sample.png sample-min.png | cut -d' ' -f2 | xargs -I@ mv @ min/
# 横幅500pxで圧縮したものを同じディレクトリへ
tinify sample.png sample-min-width.png 500
# 縦幅300pxで圧縮したものを同じディレクトリへ
tinify sample.png sample-min-height.png 300 height

以上コマンドからTinyPNGを使ってみました。
シェル難しいので、もう少し勉強したら*5続けよう。

*1:ヘッダの方がコマンドでの加工が楽なのでこちらで

*2:widthとheight両方を指定して小さい方に合わせて比率そのままにリサイズ

*3:中心からwidthとheightの分だけ切り抜く

*4:ひどい点があってもお許しを…というか教えてほしい

*5:/dev/std**や.shファイルあたりとかpipeで使う方法とか