📷

GoogleフォトとS3を繋ぐ Photos Picker App

に公開

はじめに

Google フォトから選んだ写真を S3 にアップロードする Web アプリ(Photo Picker)を作りました。当初は「Google フォトの任意の写真を自動取得してS3にアップロードすること」を狙っていましたが、Google Photos API の仕様変更により、APIによる写真取得には基本的にユーザーの UI 操作が必要になりました。本アプリでは Google Photos Picker API と Google Identity Services (GIS) をフロントエンドから利用し、ユーザーが選んだ写真を安全に S3 へ転送するところまでをWebアプリで実現しています。

私はこのアプリ経由でアップロードした画像を電子ペーパーが取得/描写し、デジタルアートフレームとして利用しています。そちらについては別の記事 AWS × e-Paperで作るデジタルアートフレームで紹介しています。

つくったもの

  • Google アカウントでブラウザからサインインし、Google Photos Picker UI でGoogleフォトの写真を選択
  • 選択した写真をブラウザが直接 S3 に PUT(presigned URL を Lambda が発行)
  • ローカルファイルのドラッグイン/選択にも対応し、同じフローでアップロード
  • ブラウザでアップロード済み画像の表示と削除の機能

システム全体像

Webアプリは CloudFront + S3 による静的ホスティングで動作します。
アプリのユーザ認証やアップロード済み画像の表示/削除はそれぞれ API Gateway + Lambdaで構成しています。

CloudFront + S3: Web アプリを配信

静的 Web アプリ (HTML/JS/CSS) をホスティング。S3 バケットは Public Access Block + CloudFront Origin Access Control (OAC) で保護しています。

API Gateway + Lambda + S3: 画像のアップロード/管理

  • API Gateway + Lambda
    • API Gateway /presign → Lambda presign: S3 へのアップロード URL を発行
    • API Gateway /uploads → Lambda manage_uploads: アップロード済み一覧の取得・削除
      ※ LambdaにてGoogle ID トークンの認証を行う。事前に許可したGoogleアカウントのみリクエストを許可
  • S3 バケット
    • S3 uploads/: オリジナル画像の保管先

記事で扱うコード

https://github.com/MasayoshiIwamoto/picker2paper/tree/main

picker2paper/
├─ cdk_photo_picker/          # 本記事で扱う Web + API
│  ├─ app.py                  # CDK エントリーポイント
│  ├─ photo_picker/app_stack.py
│  ├─ lambda/
│  │  ├─ presign/handler.py
│  │  └─ manage_uploads/handler.py
│  └─ site/
│     ├─ index.html
│     ├─ main.js
│     └─ config.js
├─ cdk_display_pipeline/      # e‑Paper 配信(別記事で解説)
└─ raspberryPi_code/          # デバイス側スクリプト(別記事で解説)

補足: cdk_display_pipeline/, raspberryPi_code/ は e-Paper を使って写真フレームを構築したい方向けのコードです。詳細は別の記事 AWS × e-Paperで作るサーバレス写真フレームを参照してください。

Web アプリ本体は picker2paper/cdk_photo_picker/site/ 以下にあります。config.js には API エンドポイントと Google OAuth クライアント ID を記述してからデプロイしてください。(詳細は後述)

Google Cloud 設定

Google Photos Picker API を利用するにあたり、Google Cloudにて APIの有効化と、OAuth クライアントの作成を行います。大まかには公式のマニュアルに従って作業を進めます。

  1. Google Cloud コンソールで新規プロジェクトを作成
  2. APIの有効化
    • [API とサービス] > [ライブラリ] から Google Photos Picker API を有効化
  3. OAuth 2.0 クライアント ID 作成
    • [API とサービス] > [認証情報] から認証情報 (OAuth クライアントID) を作成
    • アプリケーションの種類は ウェブ アプリケーション
    • 「承認済みの JavaScript 生成元」と「承認済みのリダイレクト URI」にはどちらもWebアプリの公開URL(例: https://photopicker.example.com) を登録
    • 作成時に表示されるクライアントID, クライアントシークレットを保管 (クライアントIDを後ほど利用します。)
  4. OAuth 同意画面の設定
    • [API とサービス] > [OAuth 同意画面] > [データアクセス] から「スコープを追加または削除」で https://www.googleapis.com/auth/photospicker.mediaitems.readonly を追加
    • [API とサービス] > [OAuth 同意画面] > [対象] からテストユーザーを追加
      • Googleフォトの写真をアップロードさせたい Google アカウントを追加
  5. クライアント ID の共有
    発行したクライアント ID を site/config.js と CDK コンテキスト (googleClientId) に設定します。

AWS 環境の構築

事前準備

(任意) Amazon Route 53 の DNS ゾーン

以降の証明書発効時のドメイン認証や、Webアプリを自身のドメインで公開する際にDNSゾーンが必要です。
Route 53利用の場合は$0.5/月かかります。その他のDNSサービスでも代替可能です。

AWS ACMによるサーバ証明書の作成

Webアプリの公開ドメイン(例: photopicker.example.com) 用の証明書を発行します。
サーバ証明書はCloudFrontから提示するため、us‑east‑1 リージョンで発行します。

CDK スタックの準備

picker2paper/cdk_photo_picker/
├─ app.py
├─ cdk.json
├─ requirements.txt
├─ photo_picker/app_stack.py
└─ lambda/
   ├─ presign/
   └─ manage_uploads/

デプロイ

cd picker2paper/cdk_photo_picker
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cdk bootstrap   # 初回のみ

cdk deploy PhotoPickerAppStack \
  --context domainName=photopicker.example.com \
  --context hostedZoneName=example.com \
  --context manageDns=true \
  --context siteBucketName=photopickerapp-site-ap-northeast-1-example \
  --context uploadsBucketName=photopickerapp-uploads-ap-northeast-1-example \
  --context uploadsPrefix=uploads/ \
  --context processedPrefix=processed/ \
  --context googleClientId=YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com \
  --context allowedEmailDomains="example.com" \
  --context allowedEmails="user1@example.com, user2@example.com" \
  --context cloudFrontCertificateArn=arn:aws:acm:us-east-1:123456789012:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

デプロイ後に確認する Outputs

  • SiteBucketName, UploadsBucketName : 作成された S3 バケット名
  • DistributionDomainName : CloudFront の自動割り当てドメイン
  • PresignEndpointForConfig, ManageEndpointForConfig : site/config.js に設定する API エンドポイント
  • SiteDnsRecord : manageDns=false の場合に出力される CloudFront エイリアス作成手順

Web アプリの設定(site/config.js)

Google Cloudで作成したクライアント ID と cdk deploy時のOutputs に応じて config.jsを以下のように設定します。
設定後、このconfig.jsを反映するために再度 cdk deployを実行します。

window.AppConfig = {
  google: {
    clientId: "YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com",
    scopes: [
      "https://www.googleapis.com/auth/photospicker.mediaitems.readonly"
    ],
  },
  upload: {
    presignEndpoint: "https://<api-id>.execute-api.ap-northeast-1.amazonaws.com/prod/presign",
    manageEndpoint:  "https://<api-id>.execute-api.ap-northeast-1.amazonaws.com/prod/uploads",
    s3KeyPrefix: "uploads/"
  }
};

使い方

  1. Web にアクセスし、「Google にサインイン」をクリック
  2. 「写真を選択」を押して Google Photos Picker で写真を選ぶ
  3. 「選んだ写真を S3 へアップロード」を押してアップロード
  4. 下部の一覧からアップロード済みファイルを確認(必要なら削除)


(補足) セキュリティとアクセス制御

  • Webアプリへのアクセス時にGoogleのログイン認証を求められます
  • Googleフォト連携(Google Photos Picker API)の利用は、Google Cloud設定にてテストユーザとして追加したアカウントのみに限定されます
  • API Gateway presign / uploads による画像のアップロード/参照は GoogleアカウントのID トークンを検証した上で実行します
    • allowedEmails に登録されたアカウント以外のリクエストは拒否します

まとめ

Google Photos Picker API とサーバーレス構成を組み合わせることで、ユーザー主導の写真選択フローを維持しつつ、安全に S3 へアップロードする仕組みを構築しました。フロントエンドは完全に静的なまま、Google 認証の検証や S3 署名付き URL の発行を Lambda に任せられるため、インフラ構成もシンプルにできました。


Discussion