Google Photosの無料容量15GBを有効活用！cURLでGoogle Photosへ画像アップロードするには？

この記事では、シェルスクリプトを介してGoogle Photosへ画像をアップロードする方法を紹介します。

<img width="661" alt="スクリーンショット 2016-11-20 4.40.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/38967/e3a28514-91f9-7e7b-b8ce-d10f238458ef.png">

# Google Photosをアプリケーションから使おう
Google Photosは、写真や動画をアップロードして保管できるクラウドサービスです。無料プランで15GB、月額250円で100GBのストレージが利用できます[^3]。また、最近では写真をスキャンしてGoogle Photosへアップロードする[フォトスキャンというモバイルアプリケーションが話題](http://jp.techcrunch.com/2016/11/16/20161115googles-new-app-photoscan-turns-prints-into-digital-photos/)になりました。

Google Photosの無料プランの容量を有効活用するために、cURLを使ったアップロードの方法について紹介していきます

[^3]: https://www.google.com/settings/storage?hl=ja

# Google PhotosのAPIはPicasa Web Albums Data APIで代用できる
残念なことに、APIはGoogle Photos APIという名前では提供されていません。しかし、PicasaとGoogle Photosが統合された関係か、`Picasa Web Albums Data API`が代用できます[^4]。
[<img width="545" alt="スクリーンショット 2016-11-20 4.42.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/38967/fb4ade7c-9174-ed3c-36d2-1fe030b5c899.png">](https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol)

[^4]: [Picasa Web Albums Data APIリファレンス](https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol)

## cURLで簡単！Picasa Web Albums Data APIを使うための認可手順
Picasa Web Albums Data APIを使って画像をアップロードするためには、アクセストークンと呼ばれるトークンが必要になります。

### アクセストークンの取得手順
アクセストークンを取得するには、以下の手順が必要です。本記事では、cURLを使ったコードも載せているため、10分くらいで終わります。

1. クライアントIDとクライアントシークレットを取得
1. クライアントの認可コード(Authorization Code)を取得
1. 認可コードからアクセストークンを取得

## (1)クライアントIDとクライアントシークレットを取得
まず、[Google APIs Console](https://console.developers.google.com/apis/credentials)から認証情報を追加します。

<img width="889" alt="スクリーンショット 2016-11-20 2.11.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/38967/8f0b2eb2-6d10-7c3e-8a17-eb567ec681d3.png">

以下の手順で、クライアントIDとクライアントシークレットを取得します。あとで利用するのでメモを残しておいてください。

* `認証情報を作成`から、`OAuthクライアントID`を選択
* `その他`を選択し、作成
* `クライアントID`と`クライアントシークレット`を控えておく

## (2)認可コードの取得
続いて、クライアントIDを利用して、認可コードを(Authorization Code)を取得します。認可コードを取得するためのスクリプトを用意しました。`YOUR_CLIENT_ID`には、さきほどメモしておいたクライアントIDを入れてください。

以下のスクリプトを実行すると、URIが表示されます。これは認可ページへのリンクです。ブラウザでアクセスして認可をすすめます。
認可ボタンを押すと、ページが変わって、ブラウザ上に認可コードが表示されます。認可コードはアクセストークン取得に必要なものです。このあとすぐ使うのでメモしておいてください。

```get_authorization_code.sh
CLIENT_ID="YOUR_CLIENT_ID"
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" # HTTPサーバを起動しなくてもAuthorization_Codeを取得できる
SCOPE="https://picasaweb.google.com/data/" # Google Photos APIの代わりにPicasa APIを使う
echo "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&scope=$SCOPE&access_type=offline"
```

### 余談
`REDIRECT_URI`の指定は、`urn:ietf:wg:oauth:2.0:oob`とします。これは、なんらかの事情でHTTPでListenできない状態のときに使われます[^1]。

[^1]: https://developers.google.com/api-client-library/python/auth/installed-app

## (3)認可コードから、アクセストークンを取得する
手に入れた認可コードを使って、アクセストークンを取得します。クライアントIDやクライアントシークレット、認可コードは控えていたメモにあるものを差し替えて使ってください。

```get_access_token_and_refresh_token.sh
CLIENT_ID="YOUR_CLIENT_ID" # クライアントID
CLIENT_SECRET="YOUR_CLIENT_SECRET" # クライアントシークレット
AUTHORIZATION_CODE="YOUR_CODE" # 認可コード
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" # HTTPサーバを起動しなくてもAuthorization_Codeを取得できる

curl --data "code=${AUTHORIZATION_CODE}" --data "client_id=${CLIENT_ID}" --data "client_secret=${CLIENT_SECRET}" --data "redirect_uri=${REDIRECT_URI}" --data "grant_type=authorization_code" --data "access_type=offline" https://www.googleapis.com/oauth2/v4/token
```

スクリプトを実行すると、アクセストークンとリフレッシュトークンがJSON形式で取得できます。返ってきたJSONにも書いてありますが、アクセストークンは3600秒経つと期限切れになります。アクセストークンが期限切れになった場合は、リフレッシュトークンによってアクセストークンを再取得できるので、メモしておいてください。リフレッシュトークンによる再取得の方法は記事の付録として添付してあります。

これでアップロードする準備は整いました。

## アクセストークンを使って、アップロードしてみる
画像をアップロードするシェルスクリプトを書きました。以下のスクリプトを実行すると、画像をアップロードできます。ただし、`USER_ID`や`ACCESS_TOKEN`は自分のものを入れておいてください。

```upload.sh
#! /bin/bash

USER_ID="USER_ID"
ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
ENDPOINT="https://picasaweb.google.com/data/feed/api/user/${USER_ID}"

TYPE="image/jpeg"
FILE="sample.jpg"
LENGTH=`ls -l "${FILE}" | tail -n1 | sed -E 's/ +/ /g' | cut -d' ' -f5`

curl -XPOST -H "Content-Type:${TYPE}" -H "Content-Length:${LENGTH}" -H "Slug:${FILE}" "${ENDPOINT}?access_token=${ACCESS_TOKEN}" --data-binary "@${FILE}"
```

# 特定のアルバムにアップロードしてみる
## アップロードしたいアルバムIDを自分のアルバム一覧から見つける
画像をアップロードするには、アルバムを指定するための、アルバムIDが必要です。Picasaでは、特定ユーザのアルバム一覧を取得するAPIが存在します。

以下のURIへアクセスすると、`hogehoge`さんの公開アルバム一覧が取得できます。すでにhogehogeさんとしてログインしている場合には、非公開を含むすべてのアルバム一覧が取得できます。

`https://picasaweb.google.com/data/feed/api/user/hogehoge`

自分のアルバム一覧をみたいときは、`hogehoge`の箇所を自分のユーザIDに差し替えてください。

### アップロードするアルバムIDの見つけ方
アルバム一覧のXMLから、目的のアルバムIDを探します。

アルバムの情報は以下のような形式で保存されています。目的のアルバムを見つけるには、まずアルバムタイトルで検索をかけるといいでしょう。そのあと該当したエントリーの`<id></id>`を見るか、`<gphoto:id></gphoto:id>`を見ます。これもまたあとで利用するのでメモをとっておいてください。

```各アルバムの形式.xml
<entry>
  <id>https://picasaweb.google.com/data/entry/api/user/:USERID/albumid/:ALBUMID</id>
  ...
  <title type='text'>:ALBUMTITLE</title>
  ...
  <gphoto:id>:ALBUMID</gphoto:id>
  <gphoto:user>:USERID</gphoto:user>
  ...
</entry>
```

## アクセストークンを使ってcURLからアップロード
さいごに、取得したアクセストークンやアルバムIDを利用して、スクリプトからGoogle Photosへアップロードを行います。もし、アクセストークンを取得してから1時間以上経過していたら、リフレッシュトークンを使って再発行しておいてください。

試しにfoodというアルバムに画像をアップロードしてみます。
<img width="1403" alt="スクリーンショット 2016-11-20 5.55.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/38967/838af57a-d279-0f01-288a-6d2d93d8b1c5.png">


以下のスクリプトを画像ファイル(JPEG)のある場所で作成してください。アップロードしたいファイル名は、`sample.jpg`という名前としておきます。`USER_ID`や`ALBUM_ID`、`ACCESS_TOKEN`を個人のものにしておけば、あとは実行するだけです。

```upload.sh
USER_ID="USER_ID"
ALBUM_ID="YOUR_ALBUM_ID"
ENDPOINT="https://picasaweb.google.com/data/feed/api/user/${USER_ID}/albumid/${ALBUM_ID}"
ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
TYPE="image/jpeg"
FILE="sample.jpg"
LENGTH=`ls -l "${FILE}" | tail -n1 | sed -E 's/ +/ /g' | cut -d' ' -f5`

curl -XPOST -H "Content-Type:${TYPE}" -H "Content-Length:${LENGTH}" -H "Slug:${FILE}" "${ENDPOINT}?access_token=${ACCESS_TOKEN}" --data-binary "@${FILE}"
```

スクリプトを実行すると、XMLが返ってきます。アップロードした画像の詳細情報がそこには記載されています。特に保存しておく必要はありません。Google Photosへアクセスしてアップロードされていることを確認します。

<img width="1469" alt="スクリーンショット 2016-11-20 5.58.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/38967/2635b774-ab0f-e35b-0fee-30602f9699b5.png">

## ディレクトリ内の画像を全部アップロードするスクリプト
ディレクトリ内部の画像ファイルをすべてアップロードするスクリプトを書きました。

```upload_images_in_dir.sh
#! /bin/bash
USER_ID="USER_ID"
ALBUM_ID="YOUR_ALBUM_ID"
ACCESS_TOKEN="YOUR_ACCESS_TOKEN"

ENDPOINT="https://picasaweb.google.com/data/feed/api/user/${USER_ID}/albumid/${ALBUM_ID}?access_token=${ACCESS_TOKEN}"

DIR="${HOME}/Downloads/images" # 任意のディレクトリを指定

IFS_BAK=${IFS}
IFS="
"
FILES=`ls -1 ${DIR}`
for FILE in ${FILES}
do
  if [ "${FILE}" != "" ]; then
    TYPE="UNKNOWN"
    case "${FILE}" in
      *\.bmp) TYPE="image/bmp";;
      *\.jpeg | *\.jpg) TYPE="image/jpeg";;
      *\.png) TYPE="image/png";;
      *) TYPE="UNKNOWN";;
    esac
    echo "$TYPE $FILE $LENGTH"
    if [ "${TYPE}" = "UNKNOWN" ]; then
      continue;
    fi
    LENGTH=`ls -l "${DIR}/${FILE}" | tail -n1 | sed -E 's/ +/ /g' | cut -d' ' -f5`
    curl -XPOST "${ENDPOINT}" \
      -H "Content-Type:${TYPE}" \
      -H "Content-Length:${LENGTH}" \
      -H "Slug:${FILE}" \
      --data-binary "@${DIR}/${FILE}" > /dev/null
    sleep 2s
  fi
done
IFS=$IFS_BAK
```

# 参考リンク
http://qiita.com/shin1ogawa/items/49a076f62e5f17f18fe5

# おわりに
本記事では、Google Photosへスクリプトを介してアップロードする方法を紹介しました。`cURL`を使っているため、ほかのプログラミング言語にも簡単に応用できます。

記事中で利用した、Picasa Web Album APIに興味のある方は、リファレンスを参照してみてください。
https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol


# 付録
## リフレッシュトークンからアクセストークンを取得する
アクセストークンが期限切れになった場合は、リフレッシュトークンによってアクセストークンを再取得できます。

```get_access_token_by_refresh_token.sh
CLIENT_ID="YOUR_CLIENT_ID"
CLIENT_SECRET="YOUR_CLIENT_SECRET"
REFRESH_TOKEN="YOUR_REFRESH_TOKEN"

curl --data "refresh_token=${REFRESH_TOKEN}" --data "client_id=${CLIENT_ID}" --data "client_secret=${CLIENT_SECRET}" --data "grant_type=refresh_token" https://www.googleapis.com/oauth2/v4/token
```

# おまけ
## `xmllint`で見づらいXMLを整形して表示する
ブラウザでxmlを表示するとだいぶ読みづらく、目的のアルバムが見つけづらいです。`xmllint`を利用して、見づらいXMLを整形できます。`xmllint`が入っている環境[^2]では以下のようにすると、整形されたxmlがターミナルから確認できます。

Vimを使っている人は、結果をVimに流し込むと、ハイライトがついてより見やすくなります。

[^2]: `xmllint`が入っているかどうかは、`which xmllint`で確認できます

```pretty_formatted_xml.sh
#! /bin/bash
USER_ID="YOUR_USER_ID"
RES=`curl https://picasaweb.google.com/data/feed/api/user/${USER_ID}`
echo "${RES}" | xmllint --format -
#echo "${RES}" | xmllint --format - | vim -R - # Vimを使っている人はこうすると便利
```