こんにちは、Webエンジニアの本間です。
昨年も予定通り12月25日に Ruby 2.5 がリリースされましたね。
個人的に嬉しいのは、ブロックパラメーターを経由したメソッド呼び出しが3倍早くなった改善です。
def foo(a, &b)
のようなメソッド定義はブロックを受け取ることが明示されていて好きなのですが、今回の改修でこのような書き方をやりやすくなったなぁと思っております。
さて今回は、弊社サービスの「写真サービス るくみー」と「Googleフォト」が連携できないか?という調査を兼ねて、RailsアプリケーションからGoogleフォトに写真をアップロードしてみようと思います。
作成するアプリケーション
サンプルコード
今回使用しているアプリケーションのソースコードは、以下のリポジトリで公開しています。 bitbucket.org
ローカルで動かすためには Encrypted Secrets を開き、Google APIの client_id
と client_secret
をセットする必要がありますが、それさえ実施していただければ動くと思います。
google: api: client_id: your_client_id client_secret: your_client_secret
サンプルアプリケーション
herokuを使って、上記サンプルコードのアプリケーションを動かしています。
https://fathomless-stream-32917.herokuapp.com/
突然止まる可能性はありますが、2-3ヶ月は動かしておきますので、気になる方は触っていただければと思います。
準備
Google API呼び出し用のCredentialを登録
Googleディベロッパーコンソール > APIs & Services > Credentials でOAuth用のCredentialを登録します。
- 右上のCreate Credentialsから「OAuth client ID」を選択
- Application Typeに「Web Application」を選択
- Authorized redirect URIsは、OAuthの認可が終わった後にコールバックさせるURLを登録する。今回はlocalhostでのテストのみを想定しているので
http://localhost:3000/oauth2/callback
を登録。localhost以外でテストする場合、そのURLも登録しておく。Authorized JavaScript originsは、Authorized redirect URIsのパスなしの値を指定しておく。 - この情報で作成すると画面内に「Client ID」と「Client secret」が表示されるのでメモ
下記に作成時の画面のスナップショットを貼っておきます。
写真一覧表示アプリケーションの作成
こんな感じに登録された写真を一覧表示するアプリケーションを作っておきます。
これは、過去に紹介した Rails 5.2で追加予定のActiveStorageを使ってみる を参考にすれば、比較的サクッと作れるのではないかなぁと思います。
Googleフォトへ写真をアップロード
現在、Googleフォト専用のAPIは提供されてないようです。 しかし、Googleフォトに統合されたPicasaの Picasa Web Albums Data API は残っており、こちらを使ってGoogleフォトに写真をアップロードできるようです。 今回、このAPIを使って写真をアップロードしてみます。
アクセストークンの取得
PicasaのAPIを呼び出すためには、OAuth2のフローにしたがってアクセストークンを取得する必要があります。 サーバーサイドで取得するためには こちらのドキュメント が参考になります。
認可画面へのリダイレクト
まずユーザーから認可をもらうため、Googleの認可用のページにリダイレクトさせます。
リダイレクト先は https://accounts.google.com/o/oauth2/v2/auth
なのですが、いくつかパラメーターをつけて遷移させる必要があります。
パラメーター名 | 値 |
---|---|
client_id | クレデンシャルと登録した時に表示されたclient_id |
scope | https://picasaweb.google.com/data/ |
redirect_uri | http://localhost:3000/oauth2/callback |
response_type | code |
RailsのControllerのコードはこんな感じになります。(client_idとclient_secretは、RailsのEncrypted Secretsを使って保管)
class Oauth2sController < ApplicationController def new uri = ::URI.parse('https://accounts.google.com/o/oauth2/v2/auth') params = { client_id: Rails.application.credentials.google[:api][:client_id], scope: 'https://picasaweb.google.com/data/', redirect_uri: callback_oauth2_url, response_type: 'code', } uri.query = ::URI.encode_www_form(params) redirect_to uri.to_s end def callback # snip... end end
このURLにリダイレクトさせると、ユーザーのブラウザでは以下のような表示になります。
ここで、ユーザーが「許可」のボタンを押すと、認可コードと共にリダイレクト時に指定したコールバックURL(上記の場合 http://localhost:3000/oauth2/callback
)にリダイレクトされてます。
認可コードからアクセストークンの取得
params[:code]
で認可コードを取得できます。
アクセストークンは、サーバーからGoogleへリクエストを送信することで取得します。 詳細は こちら のドキュメントに記載されています。 今回、パラメーターは以下のようにセットしています。
パラメーター名 | 値 |
---|---|
code | params[:code] の値 |
client_id | クレデンシャル生成時に表示されたclient_id |
client_secret | クレデンシャル生成時に表示されたclient_secret |
grant_type | authorization_code |
redirect_uri | http://localhost:3000/oauth2/callback |
上記パラメーターを含むPOSTリクエストを https://www.googleapis.com/oauth2/v4/token
に送信すると、レスポンスとしてJSONが返されます。
そのJSONの access_token
の値がアクセストークンになっています。
以下は、RailsのController内での処理になります。今回は、取得したアクセストークンをセッション内に保持しています。
class Oauth2sController < ApplicationController def new # snip... end def callback # snip... uri = ::URI.parse('https://www.googleapis.com/oauth2/v4/token') req_params = { code: params[:code], client_id: Rails.application.credentials.google[:api][:client_id], client_secret: Rails.application.credentials.google[:api][:client_secret], grant_type: 'authorization_code', redirect_uri: callback_oauth2_url, } res = ::Net::HTTP.post_form(uri, req_params) result = ::JSON.parse(res.body) session[:access_token] = result['access_token'] # snip... end end
これで、やっとPicasaのAPIを呼び出す準備が整いました。
PicasaのAPIで写真をアップロード
Picasa Web Albums Data API の 写真アップロード のAPIを呼び出すことで、任意の写真をアップロードすることができます。
アップロード先のURLは https://picasaweb.google.com/data/feed/api/user/userID/albumid/albumID
となっていますが、userID/albumIDをともに default
にすれば「トークン取得時に認可したユーザーのデフォルトのアルバム」にアップロードすることができます。
(2018-05-29追記) Google Photos API が発表されました。今後はこちらを使用することをオススメします。
今回は写真のメタ属性は特に指定せず、ファイル名と写真データのみで1枚だけアップロードしています。
HTTPヘッダーの Authorization
にさきほど取得したアクセストークンをセットしています。
class PhotosController < ApplicationController def upload_to_google @photo = Photo.with_attached_file.find(params[:id]) file = @photo.file.blob.download uri = ::URI.parse('https://picasaweb.google.com/data/feed/api/user/default/albumid/default') req = ::Net::HTTP::Post.new(uri) req['GData-Version'] = 3 req['Authorization'] = "Bearer #{session[:access_token]}" req['Content-Type'] = @photo.file.blob.content_type req['Content-Length'] = file.bytesize req['Slug'] = @photo.file.blob.filename req.body = file res = ::Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) } # snip... end end
上記のサーバー間通信が成功すると、以下のようにGoogleフォトに写真がアップロードされます。 また、同時に「Drop Box」というデフォルトのアルバムも生成されます。
以上で、RailsアプリからGoogleフォトに写真をアップロードすることができました。
注意点
今回使用している Picasa Web Albums Data API ですが、いつまでサポートされるかわかりません。
代替のAPIが出るまでサポートされると思っているのですが、もしかすると突然サポートを切られる可能性もあります。 (事実、2018年1月23日〜26日に一時的に写真アップロードのAPIが動作しなくなっていました)
本格的に導入を検討する場合は、上記のリスクを考慮していただければと思います。
まとめ
今回、Picasa Web Albums Data API を使い、RailsアプリからGoogleフォトに写真をアップロードすることができました。 また、APIを呼び出す過程で、OAuth2のフローに沿ったアクセストークンの取得も実施することができました。
概ねやりたいことはできるかな、という印象を持ちましたが、いかんせんPicasa APIの今後のサポート状況が不明のため、Googleからなんらかのアナウンスがあるまでは正式な採用は難しいかな、という印象を受けました。
以上が今回紹介したい内容になります。最後までご覧いただきありがとうございました。