请求管理
请求管理
Golang提供了context库允许调用者主动控制请求行为。在0.9.228版本之后,Go SDK新增了带有context参数的接口,实现对BOS请求的控制。本次主要对处理耗时可能较长的接口支持context参数控制请求。
本文主要介绍如何使用这些接口,支持通过context进行请求控制的接口包括:
- 列举桶/列举对象
- 上传对象
- 拷贝对象
- 下载对象
- 追加上传
- 抓取上传
- 分块上传/分块拷贝
带context参数的上传对象
context.WithCancel返回一个context对象及其相应的cancel方法,何时执行cancel方法由调用者决定,示例代码如下:
1func PutObjectWithContext(bosClient *bos.Client, ctx context.Context, wg *sync.WaitGroup) {
2 bucketName := "bucket-test"
3 objectName := "prefix/object1"
4 fileName := "/path/to/localfile"
5 bodyStream, err := bce.NewBodyFromFile(fileName)
6 etag, err := bosClient.PutObjectFromFileWithContext(ctx, bucketName, objectName, fileName, &api.PutObjectArgs{})
7 if err == nil {
8 fmt.Printf("put object with context success: %s\n", etag)
9 } else {
10 fmt.Printf("put object with context failed: %s\n", err.Error())
11 }
12 wg.Done()
13}
14
15func main() {
16 AK, SK := "Your Access Key", "Your Secret Key"
17 ENDPOINT := "bj.bcebos.com"
18 bosClient, _ := bos.NewClient(AK, SK, ENDPOINT)
19
20 // 构造context对象及其cancel方法
21 ctx, cancel := context.WithCancel(context.Background())
22 var wg sync.WaitGroup
23 wg.Add(1)
24 // 启动协程执行上传对象
25 go PutObjectWithContext(bosClient, ctx)
26
27 // 等待3s后调用cancel方法,取消上传对象请求
28 time.Sleep(3 * time.Second)
29 cancel()
30 wg.Wait()
31}
context还提供了 WithTimeout
和 WithDeadline
方法设置请求的控制行为,示例代码如下:
1// import "github.com/baidubce/bce-sdk-go/bce"
2
3ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
4//ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
5defer cancel()
6
7// 从本地文件上传
8etag, err := bosClient.PutObjectFromFileWithContext(ctx, bucketName, objectName, fileName, nil)
9
10// 从字符串上传
11str := "test put object"
12etag, err := bosClient.PutObjectFromStringWithContext(ctx, bucketName, objectName, str, nil)
13
14// 从字节数组上传
15byteArr := []byte("test put object")
16etag, err := bosClient.PutObjectFromBytesWithContext(ctx, bucketName, objectName, byteArr, nil)
17
18// 从数据流上传
19bodyStream, err := bce.NewBodyFromFile(fileName)
20etag, err := bosClient.PutObjectWithContext(ctx, bucketName, objectName, bodyStream, nil)
带context参数的拷贝对象
示例代码如下:
1// 1. 使用context.WithCancel方法控制请求行为
2ctx, cancel := context.WithCancel(context.Background())
3go bosClient.CopyObjectWithContext(ctx, bucketName, objectName, srcBucketName, srcObjectName, nil)
4time.Sleep(3 * time.Second)
5cancel()
6
7// 2.使用context.WithTimeout和context.WithDeadline控制请求行为
8ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
9//ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
10defer cancel()
11
12res, err := bosClient.CopyObjectWithContext(ctx, bucketName, objectName, srcBucketName, srcObjectName, nil)
13
14fmt.Println("ETag:", res.ETag, "LastModified:", res.LastModified)
带context参数的下载对象
示例代码如下:
1// 1. 使用context.WithCancel方法控制请求行为
2ctx, cancel := context.WithCancel(context.Background())
3
4// 提供Bucket和Object,直接获取一个对象
5res, err := bosClient.GetObjectWithContext(ctx, bucketName, objectName, nil)
6
7// 获取ObjectMeta
8meta := res.ObjectMeta
9
10// 获取Object的读取流(io.ReadCloser)
11stream := res.Body
12
13// 确保关闭Object读取流
14defer stream.Close()
15
16// 调用stream对象的Read方法处理Object
17...
18
19time.Sleep(3 * time.Second)
20cancel()
21
22// 2.使用context.WithTimeout和context.WithDeadline控制请求行为
23ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
24//ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
25defer cancel()
26
27res, err := bosClient.GetObjectWithContext(ctx, bucketName, objectName, nil)
28
29// 获取Object的读取流(io.ReadCloser)
30stream := res.Body
31
32// 确保关闭Object读取流
33defer stream.Close()
34
35// 调用stream对象的Read方法处理Object
36...
带context参数的追加上传
示例代码如下:
1// import "github.com/baidubce/bce-sdk-go/services/bos/api"
2
3args := new(api.AppendObjectArgs)
4
5// 1.使用context.WithCancel方法控制请求行为
6ctx, cancel := context.WithCancel(context.Background())
7go bosClient.AppendObjectWithContext(ctx, bucketName, objectName, bodyStream, args)
8time.Sleep(3 * time.Second)
9cancel()
10
11// 2.使用context.WithTimeout和context.WithDeadline控制请求行为
12ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
13//ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
14defer cancel()
15
16res, err := bosClient.AppendObjectWithContext(ctx, bucketName, objectName, bodyStream, args)
17
18fmt.Println(res.ETag) // 打印ETag
19fmt.Println(res.ContentMD5) // 打印ContentMD5
20fmt.Println(res.NextAppendOffset) // 打印NextAppendOffset
带context参数的抓取上传
示例代码如下:
1// import "github.com/baidubce/bce-sdk-go/services/bos/api"
2
3args := new(api.FetchObjectArgs)
4
5// 1.使用context.WithCancel方法控制请求行为
6ctx, cancel := context.WithCancel(context.Background())
7args.FetchMode = api.FETCH_MODE_SYNC
8go bosClient.FetchObject(bucketName, objectName, url, args)
9time.Sleep(3 * time.Second)
10cancel()
11
12// 2.使用context.WithTimeout和context.WithDeadline控制请求行为
13ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
14//ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
15defer cancel()
16
17res, err := bosClient.FetchObject(bucketName, objectName, url, args)
18
19fmt.Printf("res: %v", res) // 打印抓取结果
带context参数的分块上传
分块上传包括:初始化分块上传,上传分块,完成分块上传,其中初始化分块上传和完成分块上传一般不通过context控制请求行为,调用方式可参考Go sdk文件管理-分块上传中介绍的初始化分块上传和完成分块上传的调用方式。这里介绍通过context控制上传分块的请求,示例代码如下:
1// import "github.com/baidubce/bce-sdk-go/bce"
2// import "github.com/baidubce/bce-sdk-go/services/bos"
3// import "github.com/baidubce/bce-sdk-go/services/bos/api"
4
5file, _ := os.Open("/path/to/file.zip")
6
7// 分块大小按MULTIPART_ALIGN=1MB对齐
8partSize := (bosClient.MultipartSize +
9 bos.MULTIPART_ALIGN - 1) / bos.MULTIPART_ALIGN * bos.MULTIPART_ALIGN
10
11// 获取文件大小,并计算分块数目,最大分块数MAX_PART_NUMBER=10000
12fileInfo, _ := file.Stat()
13fileSize := fileInfo.Size()
14partNum := (fileSize + partSize - 1) / partSize
15if partNum > bos.MAX_PART_NUMBER { // 超过最大分块数,需调整分块大小
16 partSize = (fileSize + bos.MAX_PART_NUMBER + 1) / bos.MAX_PART_NUMBER
17 partSize = (partSize + bos.MULTIPART_ALIGN - 1) / bos.MULTIPART_ALIGN * bos.MULTIPART_ALIGN
18 partNum = (fileSize + partSize - 1) / partSize
19}
20
21// 创建保存每个分块上传后的ETag和PartNumber信息的列表
22partEtags := make([]api.UploadInfoType, 0)
23
24// 逐个分块上传
25for i := int64(1); i <= partNum; i++ {
26 // 计算偏移offset和本次上传的大小uploadSize
27 uploadSize := partSize
28 offset := partSize * (i - 1)
29 left := fileSize - offset
30 if left < partSize {
31 uploadSize = left
32 }
33
34 // 创建指定偏移、指定大小的文件流
35 partBody, _ := bce.NewBodyFromSectionFile(file, offset, uploadSize)
36
37 // 使用context.WithTimeout和context.WithDeadline控制请求行为
38 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
39 //ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
40 defer cancel()
41
42 // 上传当前分块
43 etag, err := bosClient.UploadPartWithContext(ctx, bucketName, objectKey, uploadId, int(i), partBody, nil)
44
45 // 保存当前分块上传成功后返回的序号和ETag
46 partEtags = append(partEtags, api.UploadInfoType{int(i), etag})
47}
带context参数的分块拷贝
分块拷贝的基本流程与分块上传类似,包括三步,分别是:初始化分块拷贝,分块拷贝和完成分块拷贝,其中第一步与第三步与分块上传相同,第二步分块拷贝使用 UploadPartCopy
方法完成,带context参数的分块拷贝方法则为 UploadPartCopyWithContext
。
初始化Multipart Upload
分块拷贝初始化参数InitiateMultipartUploadArgs
支持字段如下:
名称 | 描述 | 默认值 |
---|---|---|
CacheControl | 指定该Object被下载时的网页的缓存行为 | 无 |
ContentDisposition | 指示MIME用户代理如何显示附加的文件,打开或下载,及文件名称 | 无 |
Expires | 缓存过期时间 | 无 |
StorageClass | 指定拷贝目的对象的存储类型 | 标准存储 |
ObjectTagging | 指定拷贝目的对象的标签 | 无 |
上述初始化参数的值可以设置为拷贝源对象的元信息,也可以根据实际使用需求自行设置。
分块拷贝初始化的示例代码如下:
1// import "github.com/baidubce/bce-sdk-go/bce"
2// import "github.com/baidubce/bce-sdk-go/services/bos"
3// import "github.com/baidubce/bce-sdk-go/services/bos/api"
4
5// 获取拷贝源对象的元信息
6srcObjectMeta, err := srcClient.GetObjectMeta(srcBucketName, srcObjectName)
7
8// 将拷贝源对象的元信息设置到拷贝目的对象的初始化参数中,具体参数按需设置
9initArgs := api.InitiateMultipartUploadArgs{
10 CacheControl: srcObjectMeta.CacheControl,
11 ContentDisposition: srcObjectMeta.ContentDisposition,
12 Expires: srcObjectMeta.Expires,
13 StorageClass: srcObjectMeta.StorageClass,
14}
15
16initRes, err := api.InitiateMultipartUpload(c, destBucketName, destObjectName, srcObjectMeta.ContentType, &initArgs, c.BosContext)
分块拷贝
完成分块拷贝初始化后,执行分块拷贝,分块拷贝可以通过context进行控制,示例代码如下:
1// import "github.com/baidubce/bce-sdk-go/bce"
2// import "github.com/baidubce/bce-sdk-go/services/bos"
3// import "github.com/baidubce/bce-sdk-go/services/bos/api"
4
5size := srcObjectMeta.ContentLength
6
7// 分块大小按MULTIPART_ALIGN=1MB对齐
8partSize := (bosClient.MultipartSize + bos.MULTIPART_ALIGN - 1) / bos.MULTIPART_ALIGN * bos.MULTIPART_ALIGN
9
10// 计算分块数目,最大分块数MAX_PART_NUMBER=10000
11dstObjectSize := srcObjectMeta.ContentLength
12partNum := (dstObjectSize + partSize - 1) / partSize
13if partNum > bos.MAX_PART_NUMBER { // 超过最大分块数,需调整分块大小
14 partSize = (dstObjectSize + bos.MAX_PART_NUMBER + 1) / bos.MAX_PART_NUMBER
15 partSize = (partSize + bos.MULTIPART_ALIGN - 1) / bos.MULTIPART_ALIGN * bos.MULTIPART_ALIGN
16 partNum = (dstObjectSize + partSize - 1) / partSize
17}
18
19// 创建保存每个分块上传后的ETag和PartNumber信息的列表
20partEtags := make([]api.UploadInfoType, 0)
21
22// 逐个分块上传
23for i := 1; i <= partNum; i++ {
24 // 计算偏移offset和本次上传的大小uploadSize
25 uploadSize := partSize
26 offset := partSize * (i - 1)
27 left := fileSize - offset
28 if left < partSize {
29 uploadSize = left
30 }
31
32 partCopyArgs := api.UploadPartCopyArgs{
33 SourceRange: fmt.Sprintf("bytes=%d-%d", (i-1)*partSize, (i-1)*partSize+uploadSize-1),
34 IfMatch: srcMeta.ETag,
35 }
36
37 source := fmt.Sprintf("/%s/%s", srcBucketName, srcObjectName)
38 copyObjectResult, err := bosClient.UploadPartCopyWithContext(ctx, destBucketName, destObjectName, source, uploadId, i, args)
39
40 // 保存当前分块上传成功后返回的序号和ETag
41 partEtags = append(partEtags, api.UploadInfoType{i, copyObjectResult.ETag})
42}
完成Multipart Upload
所有分块拷贝成功后,最后执行完成分块拷贝的步骤,示例代码如下:
1// import "github.com/baidubce/bce-sdk-go/services/bos/api"
2
3completeArgs := api.CompleteMultipartUploadArgs{Parts: partEtags}
4res, err := bosClient.CompleteMultipartUploadFromStruct(destBucketName, destObjectName, initRes.UploadId, &completeArgs)
5
6// 输出结果对象的内容
7fmt.Println(res.Location)
8fmt.Println(res.Bucket)
9fmt.Println(res.Key)
10fmt.Println(res.ETag)