暑い!Webエンジニアの本間です。
早速で恐縮なのですが、半年ほど前「RailsからGoogleフォトに写真をアップロードしてみる」というエントリーを書きました。
こちらの記事では、PicasaのAPIを使って写真をアップロードしていました。 やりたいことは一通りできたのですが、記事の後半でも書いた通り、いつまでサポートされるかわからない問題がありました。
その後、2018年5月8日Googleから Google Phtos API の発表がありました。 正式なGoogleフォト専用のAPIということで、今後はこちらのAPIを使う方がよさそうです。 今回こちらのAPIを使って、前回作成したアプリケーションの改修を行い、使い勝手などを確認しようと思います。
作成するアプリケーション
- 内容は前回と同じ
- 使用するAPIをPicasa Web APIからGoogle Photos APIに変更
サンプルコード
前回と同じリポジトリに今回の修正を反映しています。
動かし方等はREADME.mdを参照してください。
動作確認
前回と同じくherokuでお試し環境も作っています。
https://frozen-citadel-57077.herokuapp.com/
準備など
- Google Developers Console でプロジェクトの作成、OAuth用のCredentialを登録するところは同じ
- Google Photos APIはデフォルトで有効化されていないので、有効化する必要があり
- 写真一覧画面に関しては、前回と全く同じなので割愛
Googleフォトへ写真をアップロード
Google Photos APIの概要
ドキュメントベースですが、Google Photos APIの概要 を確認しておきます。
- RESTにそったURI
- リクエストとレスポンスのBodyの書式がJSON(前はXML)
- 「ライブラリ」「アルバム」「メディア」「共有」という概念が整理された
- 現時点(2018年7月18日)では「Developer Preview」で公開中。そのため、APIの呼び出し可能回数が低め(1日2500回まで)に設定されている。
- アップロードされる写真は、容量無制限の「高画質」ではなく、容量制限のある「元の画質」でアップロードされる模様 https://developers.google.com/photos/library/guides/api-limits-quotas#photo-storage-quality 。ここは、今後のAPIの改善でアップロード画質を選択できるようにしてほしい
- パートナープログラム がある。APIの呼び出し回数の上限をあげる場合や、ユーザーの写真を商用利用する場合、参加が必要な模様。
アクセストークンの取得
この部分は前回とほとんど一緒なので割愛します。詳細は 前の記事 を参照ください。
一点だけ注意があります。認可画面へリダイレクトする際に指定する scope
の値が変更されており、細かい制御が可能になっています。
scopeの一覧
今回必要なのは写真のアップロードだけなので、 https://www.googleapis.com/auth/photoslibrary.appendonly
を指定します。
認可の画面はこんな感じでした。
写真のアップロード
アップロード手順を記載したガイド があるので、これに従います。 写真のアップロードのAPI呼び出しは2段階に分けられています。
1の写真アップロード(uploads)は、こんな感じのコードです。
photo
はActiveRecordオブジェクトです。画像ファイルをActiveStorageで1つ保持しています。セッションにAPIを呼び出すためのアクセストークンが格納されています。
def upload_image(photo) uri = ::URI.parse('https://photoslibrary.googleapis.com/v1/uploads') req = ::Net::HTTP::Post.new(uri) req['Authorization'] = "Bearer #{session[:access_token]}" req['Content-Type'] = 'application/octet-stream' req['X-Goog-Upload-File-Name'] = photo.file.blob.filename req.body = photo.file.blob.download res = ::Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| http.request(req) end res.is_a?(::Net::HTTPSuccess) ? res.body : res.error! end
このコードを実行すると、BodyにuploadTokenがセットされたレスポンスが返却されます。
2の一括作成(batchCreate)のコードはこんな感じです。1の結果のuploadTokenをリクエストにセットしてPOSTしています。今回は1件だけですが、複数件のuploadTokenをセットすることも可能です。
def create_media_item(upload_token) uri = ::URI.parse('https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate') req = ::Net::HTTP::Post.new(uri) req['Authorization'] = "Bearer #{session[:access_token]}" req['Content-Type'] = 'application/json' req.body = { newMediaItems: [ { description: "Google Photos API test", simpleMediaItem: { uploadToken: upload_token } } ] }.to_json res = ::Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| http.request(req) end res.is_a?(::Net::HTTPSuccess) ? res.body : res.error! end
以上で無事、Googleフォトに写真をアップロードすることができました。
(おまけ)Google Photos APIで気になったところ
Google Photos APIの中で RESTでは表現できないアクション を リソース名:動詞
で表現している箇所がありました。
今回使ったAPIの中では、メディアの一括作成のURIが https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate
となっています。
通常は https://photoslibrary.googleapis.com/v1/mediaItems/batchCreate
とかで :
ではなく /
で区切ると思うんですが、 batchCreate
の部分が動詞なのでリソースになっておらず、個人的にかなり違和感がありました。
gRPCを使えば良いという声も聞こえてきそうですが、RESTでJSONベースのAPIを維持する場合での一つの解決作を見た気がします。
もちろん、 POST https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate
というのは、やっぱ違和感は残りますが、 /
で区切るよりは少し緩和された印象です。
URIの中で :
って直接使ってよいのかはちょっと怪しい部分ですが、URI設計に頭を悩ますぐらいであれば、解決策の1つとしてはありだなーと思いました。
まとめ
今回、新しく公開されたGoogle Photos APIを使ってrailsから写真をアップロードしてみました。 写真のアップロード以外にも、アルバムや写真の一覧の取得や、公開に関する設定もできそうです。 過去試したPicasaのAPIより洗練されており、今後の拡張、サポートも十分期待できそうなため、Googleフォトとの連携が今後広まっていきそうだなと感じております。
最後までご覧いただきありがとうございました。