Skip to main content

用 aws-sdk-go-v2 连接 缤纷云 S4

连接到 S4 Endpoint

以下代码基于版本

github.com/aws/aws-sdk-go-v2 v1.24.1
github.com/aws/aws-sdk-go-v2/config v1.26.6
github.com/aws/aws-sdk-go-v2/credentials v1.16.16
github.com/aws/aws-sdk-go-v2/service/s3 v1.48.1
main.go
package main

import (
"context"
"fmt"
"log"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
)

const s3Endpoint = "https://s3.bitiful.net"

func main() {
// 替换成自己的AccessKey和SecretKey
s3AccessKey := "your-access-key"
s3SecretKey := "your-secret-key"

// 获取S3客户端
s3Client, err := getS3Client(s3AccessKey, s3SecretKey)
if err != nil {
log.Println("get s3 client failed, err=", err)
return
}
log.Println("get s3 client success", s3Client)
}

// 获取S3客户端
func getS3Client(key, secret string) (*s3.Client, error) {
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
if service == "S3" {
return aws.Endpoint{
URL: s3Endpoint,
}, nil
}
return aws.Endpoint{}, fmt.Errorf("unknown service requested")
})

customProvider := credentials.NewStaticCredentialsProvider(key, secret, "")
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithCredentialsProvider(customProvider), config.WithEndpointResolverWithOptions(customResolver))
if err != nil {
return nil, err
}
cfg.Region = "cn-east-1"
s3client := s3.NewFromConfig(cfg)
return s3client, nil
}

列出桶中对象

main.go
package main

import (
"context"
"fmt"
"log"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
)

const s3Endpoint = "https://s3.bitiful.net"

func main() {
// 替换成自己的AccessKey和SecretKey
s3AccessKey := "your-access-key"
s3SecretKey := "your-secret-key"

// 获取S3客户端
s3Client, err := getS3Client(s3AccessKey, s3SecretKey)
if err != nil {
log.Println("get s3 client failed, err=", err)
return
}

// 替换成自己的桶名
bucket := "your-bucket-name" // 例如:test

// 获取桶内文件列表
listObjsResponse, err := s3Client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{
Bucket: aws.String(bucket),
Delimiter: aws.String("/"),
MaxKeys: aws.Int32(50),
})
if err != nil {
log.Println("list objects failed, err=", err)
return
}

// 输出文件列表
for _, object := range listObjsResponse.Contents {
log.Printf("object key=%v", *object.Key)
}

log.Println("list objects success")
}

// 获取S3客户端
func getS3Client(key, secret string) (*s3.Client, error) {
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
if service == "S3" {
return aws.Endpoint{
URL: s3Endpoint,
}, nil
}
return aws.Endpoint{}, fmt.Errorf("unknown service requested")
})

customProvider := credentials.NewStaticCredentialsProvider(key, secret, "")
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithCredentialsProvider(customProvider), config.WithEndpointResolverWithOptions(customResolver))
if err != nil {
return nil, err
}
cfg.Region = "cn-east-1"
s3client := s3.NewFromConfig(cfg)
return s3client, nil
}

获取具备时效的预签名文件下载链接

main.go
package main

import (
"context"
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
)

const s3Endpoint = "https://s3.bitiful.net"

func main() {
// 替换成自己的AccessKey和SecretKey
s3AccessKey := "your-access-key"
s3SecretKey := "your-secret-key"

// 获取S3客户端
s3Client, err := getS3Client(s3AccessKey, s3SecretKey)
if err != nil {
log.Println("get s3 client failed, err=", err)
return
}

// 替换成自己的桶名和对象的key
bucket := "your-bucket-name" // 例如:test
objectKey := "your-object-key" // 例如:test.txt

// 获取 presign client
preSignClient := s3.NewPresignClient(s3Client)

// 获取预签名请求
preSignedRequest, _ := preSignClient.PresignGetObject(context.TODO(), &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(objectKey),
}, func(presignOptions *s3.PresignOptions) {
presignOptions.Expires = time.Hour // 过期时间,默认是900秒,这里修改为合适的时效,比如1小时
})

// 输出预签名URL
preSignedUrl := preSignedRequest.URL
log.Printf("get preSigned url success, preSignedUrl=%v", preSignedUrl)
}

// 获取S3客户端
func getS3Client(key, secret string) (*s3.Client, error) {
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
if service == "S3" {
return aws.Endpoint{
URL: s3Endpoint,
}, nil
}
return aws.Endpoint{}, fmt.Errorf("unknown service requested")
})

customProvider := credentials.NewStaticCredentialsProvider(key, secret, "")
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithCredentialsProvider(customProvider), config.WithEndpointResolverWithOptions(customResolver))
if err != nil {
return nil, err
}
cfg.Region = "cn-east-1"
s3client := s3.NewFromConfig(cfg)
return s3client, nil
}

在预签名链接中加入 CoreIX 媒体处理参数

因 S3 协议的预签名会将所有 Url 参数(Args)包含在内统一做签名,所以这里需要用到 Middleware 处理:

main.go
package main

import (
"context"
"fmt"
"log"
"time"

"github.com/aws/smithy-go/middleware"
smithyhttp "github.com/aws/smithy-go/transport/http"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
)

const s3Endpoint = "https://s3.bitiful.net"

func main() {
// 替换成自己的AccessKey和SecretKey
s3AccessKey := "your-access-key"
s3SecretKey := "your-secret-key"

// 获取S3客户端
s3Client, err := getS3Client(s3AccessKey, s3SecretKey)
if err != nil {
log.Println("get s3 client failed, err=", err)
return
}

// 替换成自己的桶名和对象的key
bucket := "your-bucket-name" // 例如:test
objectKey := "your-object-key" // 例如:test.txt

// 获取 presign client
preSignClient := s3.NewPresignClient(s3Client)

// 在预签名地址中 加入 CoreIX 媒体处理参数
bitifulQuery := map[string]string{"w": "500"} // 宽度为500px
// 在预签名地址中 加入「单线程限速」参数
// bitifulQuery := map[string]string{"x-amz-limit-rate": "102400"} // 102400 代表 102400字节/秒

//自定义参数, 使用ctx传递
ctx := context.WithValue(context.TODO(), "bitiful-query", bitifulQuery)

// 获取预签名请求
preSignedRequest, _ := preSignClient.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(objectKey),
}, func(presignOptions *s3.PresignOptions) {
presignOptions.Expires = time.Hour // 过期时间,默认是900秒,这里修改为合适的时效,比如1小时
presignOptions.ClientOptions = append(presignOptions.ClientOptions, func(options *s3.Options) {
// 注入自定义中间件
options.APIOptions = append(options.APIOptions, registerPresignedUrlAddBitifulQueryMiddleware)
})
})

//输出预签名URL
preSignedUrl := preSignedRequest.URL
log.Println("get preSigned url success, preSignedUrl=", preSignedUrl)
}

// 获取S3客户端
func getS3Client(key, secret string) (*s3.Client, error) {
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
if service == "S3" {
return aws.Endpoint{
URL: s3Endpoint,
}, nil
}
return aws.Endpoint{}, fmt.Errorf("unknown service requested")
})

customProvider := credentials.NewStaticCredentialsProvider(key, secret, "")
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithCredentialsProvider(customProvider), config.WithEndpointResolverWithOptions(customResolver))
if err != nil {
return nil, err
}
cfg.Region = "cn-east-1"
s3client := s3.NewFromConfig(cfg)
return s3client, nil
}

// 获取预签名url增加自定义参数
func registerPresignedUrlAddBitifulQueryMiddleware(stack *middleware.Stack) error {
// Attach the custom middleware to the beginning of the Initialize step
return stack.Build.Add(presignedUrlAddBitifulQueryMiddleware, middleware.After)
}

// 自定义中间件
var presignedUrlAddBitifulQueryMiddleware = middleware.BuildMiddlewareFunc("AddBitifulQuery", func(ctx context.Context, input middleware.BuildInput, next middleware.BuildHandler) (out middleware.BuildOutput, metadata middleware.Metadata, err error) {
bitifulQuery := ctx.Value("bitiful-query")
if bitifulQuery == nil {
return next.HandleBuild(ctx, input)
}

req, ok := input.Request.(*smithyhttp.Request)
if !ok {
return out, metadata, fmt.Errorf("unknown transport type %T", req)
}

bitifulQueryMap, ok := bitifulQuery.(map[string]string)
if !ok {
return next.HandleBuild(ctx, input)
}

// set bitiful query
query := req.URL.Query()
for key, value := range bitifulQueryMap {
query.Set(key, value)
}
req.URL.RawQuery = query.Encode()

return next.HandleBuild(ctx, input)
})

在预签名链接中加入「单线程限速」参数

S4 拓展了 x-amz-limit-rate 参数,可以对当前对象做下载的单线程限速,value 为 字节数(正整数)。

如上所述,这里仍然需要用到 Middleware 处理签名, 参考【在预签名链接中加入 CoreIX 媒体处理参数】只需修改bitifulQuery即可:

...
// 在预签名地址中 加入「单线程限速」参数
bitifulQuery := map[string]string{"x-amz-limit-rate": "102400"} // 102400 代表 102400字节/秒

//自定义参数, 使用ctx传递
ctx := context.WithValue(context.TODO(), "bitiful-query", bitifulQuery)
...