Google Cloud Storage は Google のオブジェクトストレージサービスです。データはバケットに存在し、各バケットには、アップロード、一覧取得、読み取り、削除を行うオブジェクト(ファイル)が含まれます。ほとんどの Delphi コードは JSON REST API 経由でこれにアクセスしますが、ストレージサービスは HTTP/2 上で Protocol Buffers を話す gRPC API も公開しています。sgcWebSockets Enterprise には TsgcGRPCClient の上に構築された型付き Cloud Storage gRPC クライアントが付属しているため、外部ランタイムや生成されたスタブなしで、Delphi や C++Builder からバケットとオブジェクトを操作できます。
仕組み
gRPC Storage API は storage.googleapis.com:443 に存在し、google.storage.v2.Storage サービスを話します。sgcWebSockets には完全な HTTP/2 スタックがすでに付属しているため、型付きクライアントは TLS を有効にした TsgcHTTP2Client トランスポートの上に配置されます。TsgcGRPCClient が gRPC のフレーミング、ヘッダー、タイムアウト処理を行い、HTTP/2 ストリームを開き、レスポンスとトレーラーを TsgcGRPCResponse に解析して返します。
型付きレイヤーは sgcGRPC_Google_Storage ユニットにあります。TsgcGRPCStorageListBucketsRequest、TsgcGRPCStorageListObjectsResponse、TsgcGRPCStorageBucket のように、Protocol Buffers との間で自身をシリアライズするリクエスト/レスポンスクラスを提供します。型付きプロパティを設定し、ToBytes を呼び出してリクエストペイロードを取得し、Call で送信し、LoadFromBytes でレスポンスを読み込みます。protobuf コンパイラも、手書きのフィールドタグも不要です。
認証には Google サービスアカウントを使用します。クライアントはサービスアカウントキーから JSON Web Token に署名し、それを gRPC の authorization メタデータに Bearer トークンとして提示します。これは Google Cloud がサーバー間呼び出しを認可する標準的な方法です。
トランスポートのセットアップ
gRPC チャネルは単なる HTTP/2 接続です。TsgcHTTP2Client を TLS 有効でストレージエンドポイントに向け、それを TsgcGRPCClient にアタッチして、バイナリ Protocol Buffers コンテンツタイプを選択します。
uses
sgcHTTP2_Client, sgcGRPC_Client, sgcGRPC_Types, sgcGRPC_Google_Storage;
var
HTTP2: TsgcHTTP2Client;
GRPC: TsgcGRPCClient;
begin
HTTP2 := TsgcHTTP2Client.Create(nil);
HTTP2.Host := 'storage.googleapis.com';
HTTP2.Port := 443;
HTTP2.TLS := True;
GRPC := TsgcGRPCClient.Create(nil);
GRPC.Client := HTTP2;
GRPC.ChannelOptions.ContentType := grpcProto;
GRPC.ChannelOptions.Compression := grpcNoCompression;
HTTP2.Active := True;
end;
サービスアカウントによる認証
サービスアカウントの JSON キーを読み込み、ストレージエンドポイント用に署名した JWT を構成し、得られたトークンを DefaultMetadata に配置すると、すべての呼び出しに付随します。自己署名のサービスアカウント JWT はオーディエンスにバインドされるため、トークンが受け入れられるには API_Endpoint が storage.googleapis.com を指している必要があります。
var
Cloud: TsgcHTTPGoogleCloud_PubSub_Client;
begin
Cloud := TsgcHTTPGoogleCloud_PubSub_Client.Create(nil);
Cloud.LoadSettingsFromFile('service-account.json');
Cloud.GoogleCloudOptions.Authentication := gcaJWT;
Cloud.GoogleCloudOptions.JWT.API_Endpoint := 'https://storage.googleapis.com/';
// OnAuthToken fires when the token is ready; copy it into the gRPC metadata
GRPC.DefaultMetadata.Clear;
GRPC.DefaultMetadata.Add('authorization', 'Bearer ' + Token);
end;
バケットの一覧取得
プロジェクト内のバケットを列挙するには、プロジェクトを Parent として TsgcGRPCStorageListBucketsRequest に値を入れ、シリアライズし、ListBuckets メソッドへユーナリの Call を行います。レスポンスのバイト列はそのまま TsgcGRPCStorageListBucketsResponse に読み込まれ、各バケットを名前とロケーションとともに公開します。
var
oRequest: TsgcGRPCStorageListBucketsRequest;
oResponse: TsgcGRPCResponse;
oBuckets: TsgcGRPCStorageListBucketsResponse;
i: Integer;
begin
oRequest := TsgcGRPCStorageListBucketsRequest.Create;
try
oRequest.Parent := 'projects/' + ProjectId;
oRequest.PageSize := 100;
oResponse := GRPC.Call('google.storage.v2.Storage', 'ListBuckets',
oRequest.ToBytes);
finally
oRequest.Free;
end;
if oResponse.StatusCode = grpcOK then
begin
oBuckets := TsgcGRPCStorageListBucketsResponse.Create;
try
oBuckets.LoadFromBytes(oResponse.Data);
for i := 0 to oBuckets.BucketCount - 1 do
Memo1.Lines.Add(oBuckets.Bucket(i).Name + ' (location: ' +
oBuckets.Bucket(i).Location + ')');
finally
oBuckets.Free;
end;
end;
end;
バケットの作成
バケットの作成も、TsgcGRPCStorageCreateBucketRequest を使って同じパターンに従います。親プロジェクト、新しいバケット ID、そしてバケットリソースを所有するプロジェクトを設定してから、CreateBucket を呼び出します。レスポンスには、作成された TsgcGRPCStorageBucket が含まれます。
var
oRequest: TsgcGRPCStorageCreateBucketRequest;
oResponse: TsgcGRPCResponse;
begin
oRequest := TsgcGRPCStorageCreateBucketRequest.Create;
try
oRequest.Parent := 'projects/' + ProjectId;
oRequest.BucketId := 'my-new-bucket';
oRequest.Bucket.Project := 'projects/' + ProjectId;
oResponse := GRPC.Call('google.storage.v2.Storage', 'CreateBucket',
oRequest.ToBytes);
finally
oRequest.Free;
end;
end;
バケット内のオブジェクトの一覧取得
バケット内のオブジェクトの一覧取得には TsgcGRPCStorageListObjectsRequest を使います。親はバケットリソース名で、projects/_/buckets/<bucket> の形式です。レスポンスは各オブジェクトの名前、サイズ、コンテンツタイプを提供し、必要に応じて Prefix と Delimiter を設定してフォルダーのような階層をたどることもできます。
var
oRequest: TsgcGRPCStorageListObjectsRequest;
oResponse: TsgcGRPCResponse;
oObjects: TsgcGRPCStorageListObjectsResponse;
i: Integer;
begin
oRequest := TsgcGRPCStorageListObjectsRequest.Create;
try
oRequest.Parent := 'projects/_/buckets/' + BucketName;
oRequest.PageSize := 100;
oResponse := GRPC.Call('google.storage.v2.Storage', 'ListObjects',
oRequest.ToBytes);
finally
oRequest.Free;
end;
if oResponse.StatusCode = grpcOK then
begin
oObjects := TsgcGRPCStorageListObjectsResponse.Create;
try
oObjects.LoadFromBytes(oResponse.Data);
for i := 0 to oObjects.ObjectCount - 1 do
Memo1.Lines.Add(oObjects.Object_(i).Name + ' (size: ' +
IntToStr(oObjects.Object_(i).Size) + ', type: ' +
oObjects.Object_(i).ContentType + ')');
finally
oObjects.Free;
end;
end;
end;
オブジェクトデータの読み書き
バケットとオブジェクトのメタデータに加えて、このユニットはオブジェクトのペイロードもカバーします。TsgcGRPCStorageReadObjectRequest はオブジェクトからバイト列を読み戻し、範囲ダウンロード用に省略可能な ReadOffset と ReadLimit を持ち、対応するレスポンスが生の Data バイト列を公開します。TsgcGRPCStorageWriteObjectRequest はオブジェクトのコンテンツをチャンクでアップロードします。最初のチャンクに WriteObjectSpec リソースを設定し、WriteOffset の値を増やしながら Data をプッシュし、最後のチャンクで FinishWrite をマークします。すべてのメッセージが ToBytes と LoadFromBytes を通じてシリアライズされるため、小さなファイルを 1 回の呼び出しでアップロードする場合も、大きなオブジェクトを分割してストリーミングする場合も、同じ型付きクラスが機能します。
ページネーションとエラー処理
一覧レスポンスは、さらに結果がある場合に NextPageToken を返します。それを次のリクエストの PageToken にコピーして再度呼び出すと、大きなバケットや長いオブジェクト一覧をページ送りできます。エラー側では、すべての TsgcGRPCResponse が型付きの StatusCode を持つため、grpcOK 以外の結果は何が問題だったかを正確に教えてくれます。トークンが不正または欠落している場合の grpcUNAUTHENTICATED から、バケットが見つからない場合の grpcNOT_FOUND までです。非同期処理では、OnGRPCResponse、OnGRPCError、OnGRPCException が同じ情報をイベント経由で配信します。
提供状況
型付き Google Cloud Storage gRPC クライアントは sgcWebSockets Enterprise エディションの一部です。認証、バケットの一覧取得と作成、オブジェクトの一覧取得をカバーする、すぐに実行できる完全なサンプルは Demos\21.GRPC\15.Cloud_Storage にあります。完全なリファレンスは gRPC Client 製品ページにあります。
ご質問やご意見はありますか? お問い合わせください。コードを書いた本人から返信いたします。
