之前文章用go http API实现了给ceph对象添加和修改自定义元数据。后面思考是否还有其他路径。
专门查看了aws-sdk-go中的接口(代码路径service/s3/api.go
)。其中S3客户端API中提供了一个CopyObject接口,和之前文章s3cmd的addHeader功能Go实现 中描述使用x-amz-copy-source类似,可以完成对象的拷贝任务。特别的一点,在文档说明中有这样一段话:
The method is useful when you want to inject custom logic of configuration into the the SDK's request lifecycle. Such as custom headers, or retry logic.
说明该接口可以用来添加定制化的信息。于是,验证一下这个接口完成添加自定义元数据的任务。
1、前提
- 安装Ceph RGW服务;
- 安装aws-sdk-go。
2、 aws-sdk-go提供的接口
以下接口信息均在 service/s3/api.go
中。
2.1 拷贝对象接口 CopyObject
接收输入结构体CopyObjectInput,输出CopyObjectOutput结构体和error错误。流程也很简单:
1、调用CopyObjectRequest函数创建一个请求。
2、使用aws/request.Request的Send()方法发送到指定服务,将返回结果写到out中。
func (c *S3) CopyObject(input *CopyObjectInput) (*CopyObjectOutput, error) {
req, out := c.CopyObjectRequest(input)
return out, req.Send()
}
2.2 创建拷贝对象请求接口 CopyObjectRequest
创建一个request.Request请求。设置request.Operation的结构体,包含请求的方法、路径、参数和返回等。
func (c *S3) CopyObjectRequest(input *CopyObjectInput) (req *request.Request, output *CopyObjectOutput) {
op := &request.Operation{
Name: opCopyObject,
HTTPMethod: "PUT",
HTTPPath: "/{Bucket}/{Key+}",
}
if input == nil {
input = &CopyObjectInput{}
}
output = &CopyObjectOutput{}
req = c.newRequest(op, input, output)
return
}
2.3 输入结构体 CopyObjectInput
设置拷贝对象输入参数的结构体。这个结构体比较长,截取了比较重要的几个属性。
- Bucket 目的桶名称
- CopySource 源对象的具体路径
- Key 目的对象的名称
- Metadata Metadata是一个哈希结构,存储键值对。在写添加Key-Value的时候会自动给Key添加
x-amz-meta-
的前缀。 - MetadataDirective 值为COPY或REPLACE。表示元数据是完全拷贝还是替换方式设置。
type CopyObjectInput struct {
// Bucket is a required field
Bucket *string `location:"uri" locationName:"Bucket" type:"string" required:"true"`
// CopySource is a required field
CopySource *string `location:"header" locationName:"x-amz-copy-source" type:"string" required:"true"`
// The key of the destination object. Key is a required field
Key *string `location:"uri" locationName:"Key" min:"1" type:"string" required:"true"`
// A map of metadata to store with the object in S3.
Metadata map[string]*string `location:"headers" locationName:"x-amz-meta-" type:"map"`
// Specifies whether the metadata is copied from the source object or replaced
// with metadata provided in the request.
MetadataDirective *string `location:"header" locationName:"x-amz-metadata-directive" type:"string" enum:"MetadataDirective"`
......
}
SDK封装了对应结构体的方法,如设置元数据的方法SetMetadata。
// SetMetadata sets the Metadata field's value.
func (s *CopyObjectInput) SetMetadata(v map[string]*string) *CopyObjectInput {
s.Metadata = v
return s
}
设置元数据复制参数的SetMetadataDirective。
// SetMetadataDirective sets the MetadataDirective field's value.
func (s *CopyObjectInput) SetMetadataDirective(v string) *CopyObjectInput {
s.MetadataDirective = &v
return s
}
对应输出结构体CopyObjectOutput,暂时可以不用不做介绍。
3、测试用例代码
流程简单:
1、创建会话;
2、设置请求;
3、调用接口发送请求。
3.1 创建会话
s3客户端的创建需要传递一个会话结构体Session。会话的存放了配置信息和处理句柄。
type Session struct {
Config *aws.Config
Handlers request.Handlers
options Options
}
会话结构体的初始化,需要使用aws.config 结构体(默认会去~/.aws/config读取相应的配置初始化),具体结构体可查看aws/config.go
文件中的定义。
aws.config 设置参数,如访问点Endpoint、区域、认证信息等。其中S3ForcePathStyle参数在本实验中设置成True。
S3ForcePathStyle *bool
该参数要求请求以 http://s3.amazonaws.com/BUCKET/KEY
形式发送(目前测试环境用的Ceph是这种方式)。默认S3客户端使用的形式是http://BUCKET.s3.amazonaws.com/KEY
。如果不设置请求会找不到请求去向而报错。
3.2 创建请求
创建CopyObjectInput请求,并设置自定义元数据和MetadataDirective值。
input := &s3.CopyObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(destFile),
CopySource: aws.String("/cephgo/file3"),
}
cusMeta := make(map[string]*string)
city := "New York"
cusMeta["city"] = &city
input = input.SetMetadata(cusMeta)
input = input.SetMetadataDirective("REPLACE")
3.3 发送请求
调用S3 客户端API的CopyObject方法发送请求。
ret, err := svc.CopyObject(input)
3.4 完整测试代码
附上功能测试的代码。
package main
import (
"fmt"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)
const (
authRegion = "default"
bucketName = "cephgo"
endPoint = "http://192.168.99.103:8080"
accessKey = "654321"
secretKey = "654321"
srcFile = "file3"
destFile = "file3"
)
func main() {
cred := credentials.NewStaticCredentials(accessKey, secretKey, "")
svc := s3.New(session.New(&aws.Config{
Region: aws.String(authRegion),
Endpoint: aws.String(endPoint),
Credentials: cred,
S3ForcePathStyle: aws.Bool(true),
}))
input := &s3.CopyObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(destFile),
CopySource: aws.String("/cephgo/file3"),
}
cusMeta := make(map[string]*string)
city := "New York"
cusMeta["city"] = &city
input = input.SetMetadata(cusMeta)
input = input.SetMetadataDirective("REPLACE")
ret, err := svc.CopyObject(input)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(ret.String())
}
结果
自定义标签展示。
写在最后
- 仔细研读aws的go sdk,相比较http API接口,aws提供的接口屏蔽了一些操作,更High Level一些,让代码更清晰。
- aws的接口代码提供了比较实用的注释,理解起来容易。
- 直接使用REPLACE方式会导致元数据的丢失,最标准的方式(和文章s3cmd的addHeader功能Go实现 类似):
1、通过HEAD请求获得头部信息;
2、将结果HeadObjectOutput复制到CopyObjectInput结构中,并做自定义元数据的增加或修改;
3、发送CopyObject请求。