目次
- はじめに
- アップロードの流れ
- Google Cloud Storage の準備
- 実装
- おわりに
はじめに
こんにちは、スタメンのミツモトです。
スタメンではTUNAG、FANTSというサービスを提供しており、画像の保存先として Amazon Web Services のS3(Simple Storage Service)を採用しています。
Google Cloud Storage(以下、GCS)の場合、どういう流れで画像を保存するか知りたいと思い、自分の学習として GCS を用いた署名付きURLによる画像のアップロードを実装したので紹介させていただきます。
アップロードの流れ
今回はクライアントから直接GCSへ画像をアップロードします。 通信の流れは以下になります。
Google Cloud Storage の準備
サービスアカウント
最初に、GCSのバケットにアクセスするためのサービスアカウントを作成します。 Google Cloud Platformにアクセスし、サイドバーから「IAMと管理 > サービスアカウント」を選択してください。
「サービスアカウントの作成」をクリックします。
サービスアカウント名を入力し、サービスアカウントの権限として「Cloud Storage > Storageオブジェクト管理者」を選択します。
アプリケーションで作成したサービスアカウントを利用するためキーを追加します。サービスアカウントの一覧で対象アカウントの「編集」をクリックしてください。
サービスアカウントの詳細から「鍵を追加」をクリックし、JSON形式で鍵を作成してください。ここで作成した鍵をサーバーサイドの実装時に利用します。
バケット
続いてバケットの作成をします。 サイドバーから「Storage > ブラウザ」を選択してください。
バケット名の入力、データの保存場所やストレージクラス等を選択し、バケットを作成します。
以上で Google Cloud Storage の準備は完了です。
実装
サーバーサイド
署名付きURLを発行するためのモジュールを作成します。 先程作成したサービスアカウントを用いてクレデンシャルを生成し、それを用いてバケットに対する署名付きURLを発行します。
content_typeを指定しないとクライアントからのアップロードが上手くいかないため、署名付きURLを発行する時点でcontent_typeを指定しておきます。
require 'google/cloud/storage' module Utils::Gcp::Storage GCP_SA_CREDENTIALS = { private_key: 'サービスアカウントのprivate_key', client_email: 'サービスアカウントのclient_email' } GCS_PROJECT_ID = 'GCPのプロジェクトID' GCS_BUCKET_NAME = 'GCPのバケット名' class << self def pre_signed_url(path, content_type, expires) @storage = Google::Cloud::Storage.new( project_id: GCP_PROJECT_ID, credentials: GC_SA_CREDENTIALS) expires = expires.to_i @storage.signed_url(GCS_BUCKET_NAME, path, method: 'PUT', content_type: content_type, expires: expires) end end end
Resourceであるインスタンスからモジュールのpre_signed_url
メソッドを呼び出し、レスポンスとして返却します。
class Resource < ApplicationRecord def pre_signed_url(filename【GCS上のファイル名】, content_type) Utils::Gcp::Storage.pre_signed_url("resouces/#{id}/images/" + filename, content_type, 5.minutes.from_now) end end
クライアントサイド
最初のシーケンス図通り、以下の順番でリクエストを送ります。(エラーハンドリングは省略しています。)
- 署名付きURLの取得
- GCSへの画像アップロード
- アップロードされた画像の情報をDBへ保存
実装としては以下のようになります。
const uploadImage = async(file, resourceId) => { // 署名付きURLの取得 const res = await fetch("署名付きURL取得エンドポイント" + `?content_type=${file.type}`) const resJson = await res.json(); // 署名付きURLを用いて Google Cloud Storage へアップロード await fetch(resJson.preSignedUrl , { method: "PUT", headers: { "Content-Type": file.type }, body: file }) // アップロードされた画像の情報をDBへ保存 fetch( "画像の情報をDBへ保存するエンドポイント", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" }, body: `uniq_filename=${resJson.filename}&filename=${file.name}&content_type=${file.type}&byte_size=${file.size}` } ) }
おわりに
GCS を用いた署名付きURLによる画像のアップロードについて紹介させていただきました。想定していたより簡単に画像のアップロードを実現できたので良かったです。今回はアップロードの流れを書きましたが、機会があれば画像配信についても記事にできたらと思います。
スタメンでは一緒に働くエンジニアを募集しています。 興味がある方は、ぜひ採用サイトからご連絡ください!