Google Cloud Storage 是 Google 的对象存储服务。数据存放在存储桶中,每个存储桶保存若干对象(文件),你可以上传、列出、读取和删除它们。大多数 Delphi 代码通过 JSON REST API 访问它,但该存储服务也提供一个 gRPC API,它通过 HTTP/2 使用 Protocol Buffers。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,并将它作为一个 Bearer 令牌放在 gRPC 的 authorization 元数据中,这是 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;
列出存储桶
要枚举一个项目中的存储桶,请填充一个 TsgcGRPCStorageListBucketsRequest,将项目作为 Parent,序列化它,并向 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 序列化,因此无论你是在一次调用中上传一个小文件,还是分片流式传输一个大对象,同一套强类型类都能工作。
分页和错误处理
当有更多结果可用时,列表响应会返回一个 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 产品页面。
有问题或反馈?联系我们。你会收到来自编写这段代码的人的回复。
