腾讯云的对象存储可以看做是一个线上的KV,因为最近有需求所以试着用了一下。在使用的时候遇到了一些问题,具体来说是cos.BucketGetOptions
中的Delimiter的使用问题。
按官方的说法,delimiter表示列出当前目录下的object,设置为空的时候列出所有的object。我不太清楚这个所有具体是什么意思,因此还是实践了一下。
代码与腾讯cos Go SDK使用学习比较类似,本身应该没有难度。
代码包含以下内容:
使用以下函数构造环境,注意:腾讯云对象存储不是免费服务,使用时注意费用情况。本文示例的文件操作理论上不会超过免费限制,但在修改代码的时候请注意。
func main(){
u, _ := url.Parse("https://wtytest-1252789333.cos.ap-guangzhou.myqcloud.com")
b := &cos.BaseURL{BucketURL: u}
c := cos.NewClient(b, &http.Client{
Transport: &cos.AuthorizationTransport{
// 通过环境变量获取密钥
// 环境变量 COS_SECRETID 表示用户的 SecretId,登录访问管理控制台查看密钥,https://console.cloud.tencent.com/cam/capi
SecretID: "secretID",
// 环境变量 COS_SECRETKEY 表示用户的 SecretKey,登录访问管理控制台查看密钥,https://console.cloud.tencent.com/cam/capi
SecretKey: "secret",
// Debug 模式,把对应 请求头部、请求内容、响应头部、响应内容 输出到标准输出
Transport: &debug.DebugRequestTransport{
RequestHeader: true,
RequestBody: true,
ResponseHeader: true,
ResponseBody: false,
},
},
})
// 1. 批量上传文件,构建测试集合
uploadFileToCos(0, "", c)
}
func uploadFileToCos(depth int, prefix string, c *cos.Client) {
if depth > 5 {
return
}
for i := 0; i < 3; i++ {
s := rand.Intn(100)
filePath := prefix + "file" + strconv.Itoa(s)
err := uploadFileByName(filePath, "file"+strconv.Itoa(s), c)
if err != nil {
log_status(err)
}
}
for i := 0; i < 2; i++ {
s := rand.Intn(100)
filePrefix := prefix + "path" + strconv.Itoa(s) + "/"
uploadFileToCos(depth+1, filePrefix, c)
}
}
// 随便选一个文件名,测试一下是否成功写入
//getFileByName("path59/file81", c)
// 结果为content: file81
func getFileByName(name string, c *cos.Client) {
resp, err := c.Object.Get(context.Background(), name, nil)
log_status(err)
bs, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println("content:", string(bs))
}
func main(){
const NoDelimiter = ""
const FolderDelimiter = "/"
const path1 = "path59/path33/path43/" // 测试末尾为/
const path2 = "path59/path33/path43" // 测试末尾不为/
getAllFileInPrefix(path1, NoDelimiter, c)
getAllFileInPrefix(path1, FolderDelimiter, c)
}
func getAllFileInPrefix(prefix string, delimiter string, c *cos.Client) {
totalFileCount := 0
isTruncated := true
marker := ""
count := 0
for isTruncated {
fmt.Printf("count:%d marker:%s isTruncated:%v", count, marker, isTruncated)
count++
opt := &cos.BucketGetOptions{
Prefix: prefix,
Marker: marker,
Delimiter: delimiter,
}
v, _, err := c.Bucket.Get(context.Background(), opt)
if err != nil {
log_status(err)
return
}
for _, content := range v.Contents {
resp, err := c.Object.Get(context.Background(), content.Key, nil)
if err != nil {
log_status(err)
return
}
bs, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
fmt.Printf("key:%s content:%s\n", content.Key, string(bs))
totalFileCount++
}
marker = v.NextMarker
isTruncated = v.IsTruncated
}
fmt.Println("total file count is ", totalFileCount, " with count:", count)
}
当cos的键的前缀末尾有"/“时,采用NoDelimiter能获得21个文件,采用FolderDelimiter能获得3个文件。 当cos的键的前缀末尾没有”/“时,采用NoDelimiter能获得21个文件,采用FolderDelimiter能获得0个文件。
这说明delimiter为”/“的时候可以理解为文件夹模式,文件夹模式只对模拟出的文件夹(即前缀末尾有”/")起作用。 如果delimiter为空字符串的时候则会尝试匹配前缀相同的对象。
文件列出与文件下载开始的一段逻辑有点类似,不过加上了commonPrefix。测试代码与此前相同,此处不再赘述。
func listAllFileInPrefix(prefix string, delimiter string, c *cos.Client) {
var marker string
opt := &cos.BucketGetOptions{
Prefix: prefix,
Delimiter: delimiter,
MaxKeys: 1000,
}
totalFileCount := 0
commonPrefixCount := 0
isTruncated := true
for isTruncated {
opt.Marker = marker
v, _, err := c.Bucket.Get(context.Background(), opt)
if err != nil {
fmt.Println(err)
break
}
for _, content := range v.Contents {
fmt.Printf("Object: %v\n", content.Key)
}
totalFileCount += len(v.Contents)
// common prefix表示表示被delimiter截断的路径, 如delimter设置为/, common prefix则表示所有子目录的路径
for _, commonPrefix := range v.CommonPrefixes {
fmt.Printf("CommonPrefixes: %v\n", commonPrefix)
}
commonPrefixCount += len(v.CommonPrefixes)
isTruncated = v.IsTruncated // 是否还有数据
marker = v.NextMarker // 设置下次请求的起始 key
}
fmt.Println("total file count", totalFileCount, " commonPrefix:", commonPrefixCount)
}
当cos的键的前缀末尾有"/“时,采用NoDelimiter能获得21个文件且没有截断路径,采用FolderDelimiter能获得3个文件并截断了2个路径(当前目录下的两个子目录)。 当cos的键的前缀末尾没有”/“时,采用NoDelimiter能获得21个文件且没有截断路径,采用FolderDelimiter能获得0个文件,但是截断了一个路径(path59/path33/path43/
)。
对象存储的delimiter可以开启文件夹模式来模拟一般操作系统中的文件夹,总体来说还是比较有用的。