Google Cloud Storage is Google's object storage service. Data lives in buckets, and each bucket holds objects (files) that you upload, list, read and delete. Most Delphi code reaches it through the JSON REST API, but the storage service also exposes a gRPC API that talks Protocol Buffers over HTTP/2. sgcWebSockets Enterprise ships a typed Cloud Storage gRPC client built on TsgcGRPCClient, so you can drive buckets and objects from Delphi and C++Builder without any external runtime or generated stubs.
How it works
The gRPC Storage API lives at storage.googleapis.com:443 and speaks the google.storage.v2.Storage service. sgcWebSockets already ships a complete HTTP/2 stack, so the typed client sits on top of a TsgcHTTP2Client transport with TLS enabled. TsgcGRPCClient does the gRPC framing, headers and timeout handling, opens the HTTP/2 stream, and parses the response and trailers back into a TsgcGRPCResponse.
The typed layer is in the sgcGRPC_Google_Storage unit. It gives you request and response classes such as TsgcGRPCStorageListBucketsRequest, TsgcGRPCStorageListObjectsResponse and TsgcGRPCStorageBucket that serialize themselves to and from Protocol Buffers. You set typed properties, call ToBytes to get the request payload, send it with Call, and load the reply with LoadFromBytes. No protobuf compiler, no hand-written field tags.
Authentication uses a Google service account. The client signs a JSON Web Token from the service account key and presents it as a Bearer token in the gRPC authorization metadata, which is the standard way Google Cloud authorizes server-to-server calls.
Setting up the transport
A gRPC channel is just an HTTP/2 connection. Point a TsgcHTTP2Client at the storage endpoint with TLS on, then attach it to TsgcGRPCClient and select the binary Protocol Buffers content type.
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;
Authenticating with a service account
Load the service account JSON key, configure a JWT signed for the storage endpoint, and place the resulting token in DefaultMetadata so it travels on every call. The self-signed service-account JWT is audience-bound, so the API_Endpoint must point at storage.googleapis.com for the token to be accepted.
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;
Listing buckets
To enumerate the buckets in a project, fill in a TsgcGRPCStorageListBucketsRequest with the project as Parent, serialize it, and make a unary Call to the ListBuckets method. The reply bytes load straight into a TsgcGRPCStorageListBucketsResponse, which exposes each bucket with its name and location.
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;
Creating a bucket
Creating a bucket follows the same pattern with TsgcGRPCStorageCreateBucketRequest. Set the parent project, the new bucket id, and the project that owns the bucket resource, then call CreateBucket. The response carries the created 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;
Listing objects in a bucket
Listing the objects in a bucket uses TsgcGRPCStorageListObjectsRequest. The parent is the bucket resource name, in the form projects/_/buckets/<bucket>. The response gives you each object's name, size and content type, and you can optionally set Prefix and Delimiter to walk a folder-like hierarchy.
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;
Reading and writing object data
Beyond bucket and object metadata, the unit also covers object payloads. TsgcGRPCStorageReadObjectRequest reads bytes back from an object, with optional ReadOffset and ReadLimit for ranged downloads, and the matching response exposes the raw Data bytes. TsgcGRPCStorageWriteObjectRequest uploads object content in chunks: set the WriteObjectSpec resource for the first chunk, push Data at increasing WriteOffset values, and mark FinishWrite on the last chunk. Because every message serializes through ToBytes and LoadFromBytes, the same typed classes work whether you upload a small file in one call or stream a large object in pieces.
Pagination and error handling
The list responses return a NextPageToken when more results are available. Copy it into the next request's PageToken and call again to page through large buckets or long object listings. On the error side, every TsgcGRPCResponse carries a typed StatusCode, so a non-grpcOK result tells you exactly what went wrong, from grpcUNAUTHENTICATED for a bad or missing token to grpcNOT_FOUND for a missing bucket. For asynchronous work, OnGRPCResponse, OnGRPCError and OnGRPCException deliver the same information through events.
Availability
The typed Google Cloud Storage gRPC client is part of the sgcWebSockets Enterprise edition. A complete, ready-to-run sample is in Demos\21.GRPC\15.Cloud_Storage, covering authentication, listing and creating buckets, and listing objects. The full reference is on the gRPC Client product page.
Questions or feedback? Get in touch. You will get a reply from the people who wrote the code.
