阿里云对象存储(Object Storage Service,简称OSS),是阿里云对外提供的海量、安全、低成本、高可靠的云存储服务。用户可以通过调用API,在任何应用、任何时间、任何地点上传和下载数据,也可以通过用户Web控制台对数据进行简单的管理。OSS适合存放任意文件类型,适合各种网站、开发企业及开发者使用。
该开发套件隐藏了许多较低级别的实现,例如身份验证、请求重试和错误处理, 通过其提供的接口,让您不用复杂编程即可访问阿里云OSS服务。
该开发套件同时提供实用的模块,例如上传和下载管理器,自动将大对象分成多块并行传输。
您可以参阅该指南,来帮助您安装、配置和使用该开发套件。
跳转到:
使用Go 1.18及以上版本。 请参考Go安装下载和安装Go编译运行环境。 您可以执行以下命令查看Go语言版本。
go version
在 go.mod 文件中添加以下依赖。
require (
github.com/aliyun/alibabacloud-oss-go-sdk-v2 latest
)
go get github.com/aliyun/alibabacloud-oss-go-sdk-v2
运行以下代码查看SDK版本:
package main
import (
"fmt"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
)
func main() {
fmt.Println("OSS Go SDK Version: ", oss.Version())
}
您可以配置服务客户端的常用设置,例如超时、日志级别和重试配置,大多数设置都是可选的。 但是,对于每个客户端,您必须指定区域和凭证。 SDK使用这些信息签署请求并将其发送到正确的区域。
此部分的其它主题
配置客户端的设置有多种方法,以下是推荐的模式。
package main
import (
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
func main() {
var (
// 以华东1(杭州)为例
region = "cn-hangzhou"
// 以从环境变量中获取访问凭证为例
provider credentials.CredentialsProvider = credentials.NewEnvironmentVariableCredentialsProvider()
)
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(provider).
WithRegion(region)
}
指定区域时,您可以指定向何处发送请求,例如 cn-hangzhou 或 cn-shanghai。有关所支持的区域列表,请参阅 OSS访问域名和数据中心。
SDK 没有默认区域,您需要加载配置时使用config.WithRegion
作为参数显式设置区域。例如
cfg := oss.LoadDefaultConfig().WithRegion("cn-hangzhou")
说明:该SDK默认使用v4签名,所以必须指定该参数。
SDK需要凭证(访问密钥)来签署对 OSS 的请求, 所以您需要显式指定这些信息。当前支持凭证配置如下:
SDK 支持从环境变量获取凭证,支持的环境变量名如下:
- OSS_ACCESS_KEY_ID
- OSS_ACCESS_KEY_SECRET
- OSS_SESSION_TOKEN(可选)
以下展示了如何配置环境变量。
- Linux、OS X 或 Unix
$ export OSS_ACCESS_KEY_ID=YOUR_ACCESS_KEY_ID
$ export OSS_ACCESS_KEY_SECRET=YOUR_ACCESS_KEY_SECRET
$ export OSS_SESSION_TOKEN=TOKEN
- Windows
$ set OSS_ACCESS_KEY_ID=YOUR_ACCESS_KEY_ID
$ set OSS_ACCESS_KEY_SECRET=YOUR_ACCESS_KEY_SECRET
$ set OSS_SESSION_TOKEN=TOKEN
使用环境变量凭证
provider := credentials.NewEnvironmentVariableCredentialsProvider()
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
如果你需要在阿里云的云服务器ECS中访问您的OSS,您可以通过ECS实例RAM角色的方式访问OSS。实例RAM角色允许您将一个角色关联到云服务器实例,在实例内部基于STS临时凭证通过指定方法访问OSS。
使用ECS实例角色凭证
- 指定实例角色,例如角色名为 EcsRoleExample
provider := credentials.NewEcsRoleCredentialsProvider(func(ercpo *credentials.EcsRoleCredentialsProviderOptions) {
ercpo.RamRole = "EcsRoleExample"
})
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
- 不指定实例角色
provider := credentials.NewEcsRoleCredentialsProvider()
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
当不指定实例角色名时,会自动查询角色名。
您可以在应用程序中对凭据进行硬编码,显式设置要使用的访问密钥。
注意: 请勿将凭据嵌入应用程序中,此方法仅用于测试目的。
- 长期凭证
provider := credentials.NewStaticCredentialsProvider("AKId", "AKSecrect")
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
- 临时凭证
provider := credentials.NewStaticCredentialsProvider("AKId", "AKSecrect", "Token")
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
您可以在应用程序中,通过外部进程获取凭证。
注意:
生成凭证的命令不可由未经批准的进程或用户访问,则可能存在安全风险。
生成凭证的命令不会把任何秘密信息写入 stderr 或 stdout,因为该信息可能会被捕获或记录,可能会将其向未经授权的用户公开。
外部命令返回的凭证,支持长期凭证和临时凭证,其格式如下:
- 长期凭证
{
"AccessKeyId" : "AKId",
"AccessKeySecret" : "AKSecrect",
}
- 临时凭证
{
"AccessKeyId" : "AKId",
"AccessKeySecret" : "AKSecrect",
"Expiration" : "2023-12-29T07:45:02Z",
"SecurityToken" : "token",
}
以 test-command 命令为例,该命令返回长期凭证
process := "test-command"
provider := credentials.NewProcessCredentialsProvider(process)
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
以 test-command-sts 命令为例,该命令返回临时凭证,每次请求凭证都不一样
process := "test-command-sts"
cprovider := credentials.NewProcessCredentialsProvider(process)
// NewCredentialsFetcherProvider 根据 'Expiration' 时间,自动刷新凭证
provider := credentials.NewCredentialsFetcherProvider(credentials.CredentialsFetcherFunc(func(ctx context.Context) (credentials.Credentials, error) {
return cprovider.GetCredentials(ctx)
}))
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
如果您需要授权访问或跨账号访问OSS,您可以通过RAM用户扮演对应RAM角色的方式授权访问或跨账号访问OSS。
SDK 不直接提供该访问凭证实现,需要结合阿里云凭证库credentials-go,具体配置如下:
import (
"context"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
openapicred "github.com/aliyun/credentials-go/credentials"
)
// ...
config := new(openapicred.Config).
// Which type of credential you want
SetType("ram_role_arn").
// AccessKeyId of your account
SetAccessKeyId("AccessKeyId").
// AccessKeySecret of your account
SetAccessKeySecret("AccessKeySecret").
// Format: acs:ram::USER_Id:role/ROLE_NAME
SetRoleArn("RoleArn").
// Role Session Name
SetRoleSessionName("RoleSessionName").
// Not required, limit the permissions of STS Token
SetPolicy("Policy").
// Not required, limit the Valid time of STS Token
SetRoleSessionExpiration(3600)
arnCredential, gerr := openapicred.NewCredential(config)
provider := credentials.CredentialsProviderFunc(func(ctx context.Context) (credentials.Credentials, error) {
if gerr != nil {
return credentials.Credentials{}, gerr
}
cred, err := arnCredential.GetCredential()
if err != nil {
return credentials.Credentials{}, err
}
return credentials.Credentials{
AccessKeyID: *cred.AccessKeyId,
AccessKeySecret: *cred.AccessKeySecret,
SecurityToken: *cred.SecurityToken,
}, nil
})
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
您也可以在应用或服务中使用OIDC认证访问OSS服务,关于OIDC角色SSO的更多信息,请参见OIDC角色SSO概览。
SDK 不直接提供该访问凭证实现,需要结合阿里云凭证库credentials-go,具体配置如下:
import (
"context"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
openapicred "github.com/aliyun/credentials-go/credentials"
)
// ...
config := new(openapicred.Config).
SetType("oidc_role_arn").
SetOIDCProviderArn("OIDCProviderArn").
SetOIDCTokenFilePath("OIDCTokenFilePath").
SetRoleSessionName("RoleSessionName").
SetPolicy("Policy").
SetRoleArn("RoleArn").
SetSessionExpiration(3600)
arnCredential, gerr := openapicred.NewCredential(config)
provider := credentials.CredentialsProviderFunc(func(ctx context.Context) (credentials.Credentials, error) {
if gerr != nil {
return credentials.Credentials{}, gerr
}
cred, err := arnCredential.GetCredential()
if err != nil {
return credentials.Credentials{}, err
}
return credentials.Credentials{
AccessKeyID: *cred.AccessKeyId,
AccessKeySecret: *cred.AccessKeySecret,
SecurityToken: *cred.SecurityToken,
}, nil
})
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
当以上凭证配置方式不满足要求时,您可以自定义获取凭证的方式。SDK 支持多种实现方式。
- 实现 credentials.CredentialsProvider 接口
import (
"context"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
type CustomerCredentialsProvider struct {
// TODO
}
func NewCustomerCredentialsProvider() CredentialsProvider {
return &CustomerCredentialsProvider{}
}
func (s CustomerCredentialsProvider) GetCredentials(_ context.Context) (credentials.Credentials, error) {
// 返回长期凭证
return credentials.Credentials{AccessKeyID: "id", AccessKeySecret: "secret"}, nil
// 返回临时凭证
//return credentials.Credentials{AccessKeyID: "id", AccessKeySecret: "secret", SecurityToken: "token"}, nil
}
provider := NewCustomerCredentialsProvider()
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
- 通过 credentials.CredentialsProviderFunc
credentials.CredentialsProviderFunc 是 credentials.CredentialsProvider 的 易用性封装。
provider := credentials.CredentialsProviderFunc(func(ctx context.Context) (credentials.Credentials, error) {
// 返回长期凭证
return credentials.Credentials{AccessKeyID: "id", AccessKeySecret: "secret"}, nil
// 返回临时凭证
//return credentials.Credentials{AccessKeyID: "id", AccessKeySecret: "secret", SecurityToken: "token"}, nil
})
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
- 通过 credentials.CredentialsFetcherFunc
credentials.CredentialsFetcherFunc 是 credentials.CredentialsFetcher 易用性接口。
credentials.CredentialsFetcher 具备 根据 'Expiration' 时间,自动刷新凭证的能力,当您需要定期更新凭证时,请使用该方式。
customerProvider := credentials.CredentialsProviderFunc(func(ctx context.Context) (credentials.Credentials, error) {
var (
akId string
akSecret string
token string
expires *time.Time
)
// 获取临时凭证 和 凭证的过期时间
...
// 遇到错误
if err != nil {
return credentials.Credentials{}, err
}
// 成功
return credentials.Credentials{AccessKeyID: akId, AccessKeySecret: akSecret, SecurityToken: token, Expires: expires}, nil
})
provider := credentials.CredentialsProviderFunc(func(ctx context.Context) (credentials.Credentials, error) {
return customerProvider.GetCredentials()
})
cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider)
您可以通过Endpoint参数,自定义服务请求的访问域名。
当不指定时,SDK根据Region信息,构造公网访问域名。例如当Region为'cn-hangzhou'时,构造出来的访问域名为'oss-cn-hangzhou.aliyuncs.com'。
您可以通过修改配置参数,构造出其它访问域名,例如 内网访问域名,传输加速访问域名 和 双栈(IPV6,IPV4)访问域名。有关OSS访问域名规则,请参考OSS访问域名使用规则。
当通过自定义域名访问OSS服务时,您需要指定该配置参数。在使用自定义域名发送请求时,请先绑定自定域名至Bucket默认域名,具体操作详见 绑定自定义域名。
以 访问 Region 'cn-hangzhou' 为例
- 使用公网域名
cfg := oss.LoadDefaultConfig().
WithRegion("cn-hangzhou")
或者
cfg := oss.LoadDefaultConfig().
WithRegion("cn-hangzhou").
WithEndpoint("oss-cn-hanghzou.aliyuncs.com")
- 使用内网域名
cfg := oss.LoadDefaultConfig().
WithRegion("cn-hangzhou").
WithUseInternalEndpoint(true)
或者
cfg := oss.LoadDefaultConfig().
WithRegion("cn-hangzhou").
WithEndpoint("oss-cn-hanghzou-internal.aliyuncs.com")
- 使用传输加速域名
cfg := oss.LoadDefaultConfig().
WithRegion("cn-hangzhou").
WithUseAccelerateEndpoint(true)
或者
cfg := oss.LoadDefaultConfig().
WithRegion("cn-hangzhou").
WithEndpoint("oss-accelerate.aliyuncs.com")
- 使用双栈域名
cfg := oss.LoadDefaultConfig().
WithRegion("cn-hangzhou").
WithUseDualStackEndpoint(true)
或者
cfg := oss.LoadDefaultConfig().
WithRegion("cn-hangzhou").
WithEndpoint("cn-hangzhou.oss.aliyuncs.com")
以 'www.example-***.com' 域名 绑定到 'cn-hangzhou' 区域 的 bucket-example 存储空间为例
cfg := oss.LoadDefaultConfig().
WithRegion("cn-hangzhou").
WithEndpoint("www.example-***.com").
WithUseCName(true)
var (
region = "YOUR Region"
endpoint = "YOUR Endpoint"
)
cfg := oss.LoadDefaultConfig().
WithRegion(region).
WithEndpoint(endpoint)
在大多数情况下,使用具有默认值的默认HTTP客户端 能够满足业务需求。您也可以更改HTTP 客户端,或者更改 HTTP 客户端的默认配置,以满足特定环境下的使用需求。
本部分将介绍如何设置 和 创建 HTTP 客户端。
通过config修改常用的配置,支持参数如下:
参数名字 | 说明 | 示例 |
---|---|---|
ConnectTimeout | 建立连接的超时时间, 默认值为 5 秒 | WithConnectTimeout(10 * time.Second) |
ReadWriteTimeout | 应用读写数据的超时时间, 默认值为 10 秒 | WithReadWriteTimeout(30 * time.Second) |
InsecureSkipVerify | 是否跳过SSL证书校验,默认检查SSL证书 | WithInsecureSkipVerify(true) |
EnabledRedirect | 是否开启HTTP重定向, 默认不开启 | WithEnabledRedirect(true) |
ProxyHost | 设置代理服务器 | WithProxyHost("http://user:[email protected]***.com") |
ProxyFromEnvironment | 通过环境变量设置代理服务器 | WithProxyFromEnvironment(true) |
UploadBandwidthlimit | 整体的上传带宽限制,单位为 KiB/s | WithUploadBandwidthlimit(10*1024) |
DownloadBandwidthlimit | 整体的下载带宽限制,单位为 KiB/s | WithDownloadBandwidthlimit(10*1024) |
示例
cfg := oss.LoadDefaultConfig().
WithConnectTimeout(10 * time.Second).
UploadBandwidthlimit(10*1024)
当常用配置参数无法满足场景需求时,可以使用 WithHTTPClient 替换默认的 HTTP 客户端。
在以下示例未提到的设置参数,请参考 Transport 文档。
import (
"crypto/tls"
"net/http"
"time"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/transport"
)
...
// 常用超时或其它设置
transConfig := transport.Config{
// 连接超时, 默认值 5秒
//ConnectTimeout: oss.Ptr(10 * time.Second),
// 应用读写数据的超时时间, 默认值 10秒
//ReadWriteTimeout: oss.Ptr(20 * time.Second),
// 连接空闲超时时间, 默认值 50 秒
//IdleConnectionTimeout: oss.Ptr(40 * time.Second),
// 网络连接的保持期限, 默认值 30 秒
//KeepAliveTimeout: oss.Ptr(40 * time.Second),
// 是否打开启HTTP重定向,默认不启用
//EnabledRedirect: oss.Ptr(true),
}
// http.Transport 设置
var transports []func(*http.Transport)
// 最大连接数,默认值 100
//transports = append(transports, transport.MaxConnections(200))
// 如果请求有“Expect: 100-Continue”标头,则此设置表示完全写入请求标头后等待服务器第一个响应标头的最长时间,默认 1秒
//transports = append(transports, transport.ExpectContinueTimeout(2*time.Second))
// TLS的最低版本,默认值 TLS 1.2
//transports = append(transports, transport.TLSMinVersion(tls.VersionTLS13))
// 是否跳过证书检查,默认不跳过
//transports = append(transports, transport.InsecureSkipVerify(true))
// 其它 http.Transport 参数设置
//transports = append(transports, func(t *http.Transport) {
// t.DisableCompression
//})
customClient := transport.NewHttpClient(&transConfig, transports...)
cfg := oss.LoadDefaultConfig().WithHttpClient(customClient)
您可以配置对HTTP请求的重试行为。
当没有配置重试策略时,SDK 使用 retry.Standard 作为客户端的默认实现,其默认配置如下:
参数名称 | 说明 | 默认值 |
---|---|---|
MaxAttempts | 最大尝试次数 | 3 |
MaxBackoff | 最大退避时间 | 20秒, 20 * time.Second |
BaseDelay | 基础延迟 | 200毫秒, 200 * time.Millisecond |
Backoff | 退避算法 | FullJitter 退避, [0.0, 1.0) * min(2 ^ attempts * baseDealy, maxBackoff) |
ErrorRetryables | 可重试的错误 | 具体的错误信息,请参见重试错误 |
当发生可重试错误时,将使用其提供的配置来延迟并随后重试该请求。请求的总体延迟会随着重试次数而增加,如果默认配置不满足您的场景需求时,需要配置重试参数 或者修改重试实现。
您可以通过以下两种方式修改最大尝试次数。例如 最多尝试 5 次
cfg := oss.LoadDefaultConfig().WithRetryMaxAttempts(5)
或者
cfg := oss.LoadDefaultConfig().WithRetryer(retry.NewStandard(func(ro *retry.RetryOptions) {
ro.MaxAttempts = 5
}))
例如 调整 BaseDelay 为 500毫秒,最大退避时间为 25秒
cfg := oss.LoadDefaultConfig().WithRetryer(retry.NewStandard(func(ro *retry.RetryOptions) {
ro.MaxBackoff = 25 * time.Second
ro.BaseDelay = 500 * time.Millisecond
}))
例如 使用固定时间退避算法,每次延迟2秒
cfg := oss.LoadDefaultConfig().WithRetryer(retry.NewStandard(func(ro *retry.RetryOptions) {
ro.Backoff = retry.NewFixedDelayBackoff(2 * time.Second)
}))
例如 在原有基础上,新增自定义可重试错误
type CustomErrorCodeRetryable struct {
}
func (*CustomErrorCodeRetryable) IsErrorRetryable(err error) bool {
// 判断错误
// return true
return false
}
errorRetryables := retry.DefaultErrorRetryables
errorRetryables = append(errorRetryables, &CustomErrorCodeRetryable{})
cfg := oss.LoadDefaultConfig().WithRetryer(retry.NewStandard(func(ro *retry.RetryOptions) {
ro.ErrorRetryables = errorRetryables
}))
当您希望禁用所有重试尝试时,可以使用 retry.NopRetryer 实现
cfg := oss.LoadDefaultConfig().WithRetryer(&retry.NopRetryer{})
为了方便追查问题,SDK提供了日志记录功能,您可以在应用程序中启用调试信息以调试和诊断请求问题。
当需要启用日志记录功能时,您需要配置日志级别。当不设置日志接口时,默认将日志信息发送到进程的标准输出(stdout).
日志级别:oss.LogError, oss.LogWarn, oss.LogInfo, oss.LogDebug
日志接口: oss.LogPrinter, oss.LogPrinterFunc
例如,开启日志功能,设置日志级别为 Info,输出到标准错误输出(stderr)
cfg := oss.LoadDefaultConfig().
WithLogLevel(oss.LogInfo).
WithLogPrinter(oss.LogPrinterFunc(func(a ...any) {
fmt.Fprint(os.Stderr, a...)
}))
支持的配置参数:
参数名字 | 说明 | 示例 |
---|---|---|
Region | (必选)请求发送的区域, 必选 | WithRegion("cn-hangzhou") |
CredentialsProvider | (必选)设置访问凭证 | WithCredentialsProvider(provider) |
Endpoint | 访问域名 | WithEndpoint("oss-cn-hanghzou.aliyuncs.com") |
HttpClient | HTTP客户都端 | WithHttpClient(customClient) |
RetryMaxAttempts | HTTP请求时的最大尝试次数, 默认值为 3 | WithRetryMaxAttempts(5) |
Retryer | HTTP请求时的重试实现 | WithRetryer(customRetryer) |
ConnectTimeout | 建立连接的超时时间, 默认值为 5 秒 | WithConnectTimeout(10 * time.Second) |
ReadWriteTimeout | 应用读写数据的超时时间, 默认值为 10 秒 | WithReadWriteTimeout(30 * time.Second) |
InsecureSkipVerify | 是否跳过SSL证书校验,默认检查SSL证书 | WithInsecureSkipVerify(true) |
EnabledRedirect | 是否开启HTTP重定向, 默认不开启 | WithEnabledRedirect(true) |
ProxyHost | 设置代理服务器 | WithProxyHost("http://user:[email protected]***.com") |
ProxyFromEnvironment | 通过环境变量设置代理服务器 | WithProxyFromEnvironment(true) |
UploadBandwidthlimit | 整体的上传带宽限制,单位为 KiB/s | WithUploadBandwidthlimit(10*1024) |
DownloadBandwidthlimit | 整体的下载带宽限制,单位为 KiB/s | WithDownloadBandwidthlimit(10*1024) |
SignatureVersion | 签名版本,默认值为v4 | WithSignatureVersion(oss.SignatureVersionV1) |
LogLevel | 设置日志级别 | WithLogLevel(oss.LogInfo) |
LogPrinter | 设置日志打印接口 | WithLogPrinter(customPrinter) |
DisableSSL | 不使用https请求,默认使用https | WithDisableSSL(true) |
UsePathStyle | 使用路径请求风格,即二级域名请求风格,默认为bucket托管域名 | WithUsePathStyle(true) |
UseCName | 是否使用自定义域名访问,默认不使用 | WithUseCName(true) |
UseDualStackEndpoint | 是否使用双栈域名访问,默认不使用 | WithUseDualStackEndpoint(true) |
UseAccelerateEndpoint | 是否使用传输加速域名访问,默认不使用 | WithUseAccelerateEndpoint(true) |
UseInternalEndpoint | 是否使用内网域名访问,默认不使用 | WithUseInternalEndpoint(true) |
DisableUploadCRC64Check | 上传时关闭CRC64校验,默认开启CRC64校验 | WithDisableUploadCRC64Check(true) |
DisableDownloadCRC64Check | 下载时关闭CRC64校验,默认开启CRC64校验 | WithDisableDownloadCRC64Check(true) |
AdditionalHeaders | 指定额外的签名请求头,V4签名下有效 | WithAdditionalHeaders([]string{"content-length"}) |
UserAgent | 指定额外的User-Agent信息 | WithUserAgent("user identifier") |
本部分介绍SDK提供的接口, 以及如何使用这些接口。
此部分的其它主题
SDK 提供了 与 REST API 对应的接口,把这类接口叫做 基础接口 或者 低级别API。您可以通过这些接口访问OSS的服务,例如创建存储空间,更新和删除存储空间的配置等。
这些接口采用了相同的命名规则,其接口定义如下:
func (c *Client) <OperationName>(ctx context.Context, request *<OperationName>Request, optFns ...func(*Options)) (result *<OperationName>Result, err error)
参数列表:
参数名 | 类型 | 说明 |
---|---|---|
ctx | context.Context | 请求的上下文,可以用来设置请求的总时限 |
request | *<OperationName>Request | 设置具体接口的请求参数,例如bucket,key |
optFns | ...func(*Options) | (可选)接口级的配置参数, 例如修改此次调用接口时读写超时 |
返回值列表:
返回值名 | 类型 | 说明 |
---|---|---|
result | *<OperationName>Result | 接口返回值,当 err 为nil 时有效 |
err | error | 请求的状态,当请求失败时,err 不为 nil |
指针参数:
'<OperationName>Request' 类型里,输入参数需要通过指针方式传递给接口,'<OperationName>Result' 类型里,返回参数需要通过指针方式返回给调用者。例如 ListObjctsRequest 和 ListObjectsResult:
type ListObjectsRequest struct {
Bucket *string `input:"host,bucket,required"`
Delimiter *string `input:"query,delimiter"`
...
RequestPayer *string `input:"header,x-oss-request-payer"`
RequestCommon
}
type ListObjectsResult struct {
Name *string `xml:"Name"`
Prefix *string `xml:"Prefix"`
...
ResultCommon
}
为了方便接口调用,SDK 提供了辅助函数'oss.Ptr'完成非指针类型到指针类型的转换,同时也提供辅助函数'oss.To<Type>' 安全地从指针类型转换成非指针类型。 例如 oss.Ptr函数把 string 转成 *string, 相反, oss.ToString 从 *string 转成 string。
示例:
- 创建存储空间
client := oss.NewClient(cfg)
result, err := client.PutBucket(context.TODO(), &oss.PutBucketRequest{
Bucket: oss.Ptr("bucket"),
Acl: oss.BucketACLPrivate,
ResourceGroupId: oss.Ptr("resource-group-id"),
CreateBucketConfiguration: &oss.CreateBucketConfiguration{
StorageClass: oss.StorageClassIA,
},
})
if err != nil {
log.Fatalf("failed to PutBucket %v", err)
}
fmt.Printf("PutBucket result:%v", result)
- 拷贝对象, 同时设置接口级的读写超时
client := oss.NewClient(cfg)
result, err := client.CopyObject(context.TODO(),
&oss.CopyObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
SourceBucket: oss.Ptr("source-bucket"),
SourceKey: oss.Ptr("source-key"),
},
func(o *oss.Options) {
o.OpReadWriteTimeout = oss.Ptr(30 * time.Second)
},
)
if err != nil {
log.Fatalf("failed to PutBucket %v", err)
}
fmt.Printf("CopyObject result, etg:%v", oss.ToString(result.ETag))
更多的示例,请参考 sample 目录
您可以使用预签名接口生成预签名URL,授予对存储空间中对象的限时访问权限,或者允许他人将特定对象的上传到存储空间。在过期时间之前,您可以多次使用预签名URL。
预签名接口定义如下:
func (c *Client) Presign(ctx context.Context, request any, optFns ...func(*PresignOptions)) (result *PresignResult, err error)
参数列表:
参数名 | 类型 | 说明 |
---|---|---|
ctx | context.Context | 请求的上下文 |
request | any | 设置需要生成签名URL的接口名,和 '<OperationName>Request' 一致 |
optFns | ...func(*PresignOptions) | (可选),设置过期时间,如果不指定,默认有效期为15分钟 |
返回值列表:
返回值名 | 类型 | 说明 |
---|---|---|
result | *PresignResult | 返回结果,包含 预签名URL,HTTP 方法,过期时间 和 参与签名的请求头 |
err | error | 请求的状态,当请求失败时,err 不为 nil |
request参数支持的类型:
类型 | 对应的接口 |
---|---|
*GetObjectRequest | GetObject |
*PutObjectRequest | PutObject |
*HeadObjectRequest | HeadObject |
*InitiateMultipartUploadRequest | InitiateMultipartUpload |
*UploadPartRequest | UploadPartRequest |
*CompleteMultipartUploadRequest | CompleteMultipartUploadRequest |
*AbortMultipartUploadRequest | AbortMultipartUploadRequest |
PresignOptions选项
选项值 | 类型 | 说明 |
---|---|---|
Expires | time.Duration | 从当前时间开始,多长时间过期。例如 设置一个有效期为30分钟,30 * time.Minute |
Expiration | time.Time | 绝对过期时间 |
注意: 在签名版本4下,有效期最长为7天。同时设置 Expiration 和 Expires时,优先取 Expiration。
PresignResult返回值:
参数名 | 类型 | 说明 |
---|---|---|
Method | string | HTTP 方法,和 接口对应,例如GetObject接口,返回 GET |
URL | string | 预签名 URL |
Expiration | time.Time | 签名URL的过期时间 |
SignedHeaders | map[string]string | 被签名的请求头,例如PutObject接口,设置了Content-Type 时,会返回 Content-Type 的信息。 |
示例
- 为对象生成预签名 URL,然后下载对象(GET 请求)
client := oss.NewClient(cfg)
result, err := client.Presign(context.TODO(), &oss.GetObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
})
resp, err := http.Get(result.URL)
- 为上传生成预签名 URL, 设置自定义元数据,有效期为10分钟,然后上传文件(PUT 请求)
client := oss.NewClient(cfg)
result, err := client.Presign(context.TODO(), &oss.PutObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
Metadata: map[string]string{"user": "jack"}},
oss.PresignExpires(10*time.Minute),
)
req, _ := http.NewRequest(result.Method, result.URL, nil)
for k, v := range result.SignedHeaders {
req.Header.Add(k, v)
}
resp, err := http.DefaultClient.Do(req)
更多的示例,请参考 sample 目录
对于列举类接口,当响应结果太大而无法在单个响应中返回时,都会返回分页结果,该结果同时包含一个用于检索下一页结果的标记。当需要获取下一页结果时,您需要在发送请求时设置该标记。
对常用的列举接口,V2 SDK 提供了分页器(Paginator),支持自动分页,当进行多次调用时,自动为您获取下一页结果。使用分页器时,您只需要编写处理结果的代码。
分页器 包含了 分页器对象 '<OperationName>Paginator' 和 分页器创建方法 'New<OperationName>Paginator'。分页器创建方法返回一个分页器对象,该对象实现了 'HasNext' 和 'NextPage' 方法,分别用于判断是否还有更多页, 并调用操作来获取下一页。
分页器创建方法 'New<OperationName>Paginator' 里的 request 参数类型 与 '<OperationName>' 接口中的 reqeust 参数类型一致。
'<OperationName>Paginator.NextPage' 返回的结果类型 和 '<OperationName>' 接口 返回的结果类型 一致。
type <OperationName>Paginator struct {
...
}
func (p *<OperationName>Paginator) HasNext() bool {
...
}
func (p *<OperationName>Paginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*<OperationName>Result, error) {
...
}
func (c *Client) New<OperationName>Paginator(request *<OperationName>Request, optFns ...func(*PaginatorOptions)) *<OperationName>Paginator
支持的分页器对象如下:
分页器对象 | 创建方法 | 对应的列举接口 |
---|---|---|
ListObjectsPaginator | NewListObjectsPaginator | ListObjects, 列举存储空间中的对象信息 |
ListObjectsV2Paginator | NewListObjectsV2Paginator | ListObjectsV2, 列举存储空间中的对象信息 |
ListObjectVersionsPaginator | NewListObjectVersionsPaginator | ListObjectVersions, 列举存储空间中的对象版本信息 |
ListBucketsPaginator | NewListBucketsPaginator | ListBuckets, 列举存储空间 |
ListPartsPaginator | NewListPartsPaginator | ListParts, 列举指定Upload ID所属的所有已经上传成功分片 |
ListMultipartUploadsPaginator | NewListMultipartUploadsPaginator | ListMultipartUploads, 列举存储空间中的执行中的分片上传事件 |
PaginatorOptions 选项说明:
参数 | 说明 |
---|---|
Limit | 指定返回结果的最大数 |
以 ListObjects 为例,分页器遍历所有对象 和 手动分页遍历所有对象 对比
// 分页器遍历所有对象
...
client := oss.NewClient(cfg)
p := client.NewListObjectsPaginator(&oss.ListObjectsRequest{
Bucket: oss.Ptr("examplebucket"),
})
for p.HasNext() {
page, err := p.NextPage(context.TODO())
if err != nil {
log.Fatalf("failed to get page %v", err)
}
for _, b := range page.Contents {
fmt.Printf("Object:%v, %v, %v\n", oss.ToString(b.Key), oss.ToString(b.StorageClass), oss.ToTime(b.LastModified))
}
}
// 手动分页遍历所有对象
...
client := oss.NewClient(cfg)
var marker *string
for {
result, err := client.ListObjects(context.TODO(), &oss.ListObjectsRequest{
Bucket: oss.Ptr("examplebucket"),
Marker: marker,
})
if err != nil {
log.Fatalf("failed to ListObjects %v", err)
}
for _, b := range result.Contents {
fmt.Printf("Object:%v, %v, %v\n", oss.ToString(b.Key), oss.ToString(b.StorageClass), oss.ToTime(b.LastModified))
}
if result.IsTruncated {
marker = result.NextMarker
} else {
break
}
}
针对大文件的传输场景,新增了 'Uploader','Downloader' 和 'Copier' 模块,分别管理对象的 上传,下载 和 拷贝。
上传管理器 利用分片上传接口,把大文件或者流分成多个较小的分片并发上传,提升上传的性能。
针对文件的上传场景,还提供了断点续传的能力,即在上传过程中,记录已完成的分片状态,如果出现网络中断、程序异常退出等问题导致文件上传失败,甚至重试多次仍无法完成上传,再次上传时,可以通过断点记录文件恢复上传。
type Uploader struct {
...
}
func (c *Client) NewUploader(optFns ...func(*UploaderOptions)) *Uploader
func (u *Uploader) UploadFrom(ctx context.Context, request *PutObjectRequest, body io.Reader, optFns ...func(*UploaderOptions)) (*UploadResult, error)
func (u *Uploader) UploadFile(ctx context.Context, request *PutObjectRequest, filePath string, optFns ...func(*UploaderOptions)) (*UploadResult, error)
参数列表:
参数名 | 类型 | 说明 |
---|---|---|
ctx | context.Context | 请求的上下文 |
request | *PutObjectRequest | 上传对象的请求参数,和 PutObject 接口的 请求参数一致 |
body | io.Reader | 需要上传的流。当 body 只支持io.Reader类型,必须先把数据缓冲在内存中,然后才能上传该部分。当 body 同时支持 io.Reader, io.Seeker 和 io.ReaderAt 类型时,不需要把数据缓存在内存里。 |
filePath | string | 本地文件路径 |
optFns | ...func(*UploaderOptions) | (可选),配置选项 |
UploaderOptions选项说明:
参数 | 类型 | 说明 |
---|---|---|
PartSize | int64 | 指定分片大小,默认值为 6MiB |
ParallelNum | int | 指定上传任务的并发数,默认值为 3。针对的是单次调用的并发限制,而不是全局的并发限制 |
LeavePartsOnError | bool | 当上传失败时,是否保留已上传的分片,默认不保留 |
EnableCheckpoint | bool | 是否记录断点上传信息,默认不记录 |
CheckpointDir | string | 指定记录文件的保存路径,例如 /local/dir/, 当EnableCheckpoint 为 true时有效 |
当使用NewUploader实例化实例时,您可以指定多个配置选项来自定义对象的上传行为。也可以在每次调用上传接口时,指定多个配置选项来自定义每次上传对象的行为。
设置Uploader的配置参数
u := client.NewUploader(func(uo *oss.UploaderOptions) {
uo.PartSize = 10 * 1024 * 1024
})
设置每次上传请求的配置参数
request := &oss.PutObjectRequest{Bucket: oss.Ptr("bucket"), Key: oss.Ptr("key")}
result, err := u.UploadFile(context.TODO(), request, "/local/dir/example", func(uo *oss.UploaderOptions) {
uo.PartSize = 10 * 1024 * 1024
})
示例
- 使用 Uploader上传流
...
client := oss.NewClient(cfg)
u := client.NewUploader()
var r io.Reader
// TODO 绑定io.Reader 实例到 r
result, err := u.UploadFrom(context.TODO(),
&oss.PutObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
},
r,
)
if err != nil {
log.Fatalf("failed to UploadFile %v", err)
}
fmt.Printf("upload done, etag %v\n", oss.ToString(result.ETag))
- 使用 Uploader上传文件
...
client := oss.NewClient(cfg)
u := client.NewUploader()
result, err := u.UploadFile(context.TODO(),
&oss.PutObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
},
"/local/dir/example",
)
if err != nil {
log.Fatalf("failed to UploadFile %v", err)
}
fmt.Printf("upload done, etag %v\n", oss.ToString(result.ETag))
- 上传文件,并开启断点续传功能
...
client := oss.NewClient(cfg)
u := client.NewUploader(func(uo *oss.UploaderOptions) {
uo.CheckpointDir = "/local/dir/"
uo.EnableCheckpoint = true
})
result, err := u.UploadFile(context.TODO(),
&oss.PutObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
},
"/local/dir/example"
)
if err != nil {
log.Fatalf("failed to UploadFile %v", err)
}
fmt.Printf("upload done, etag %v\n", oss.ToString(result.ETag))
下载管理器 利用范围下载,把大文件分成多个较小的分片并发下载,提升下载的性能。
该接口提供了断点续传的能力,即在下载过程中,记录已完成的分片状态,如果出现网络中断、程序异常退出等问题导致文件下载失败,甚至重试多次仍无法完成下载,再次下载时,可以通过断点记录文件恢复下载。
type Downloader struct {
...
}
func (c *Client) NewDownloader(optFns ...func(*DownloaderOptions)) *Downloader
func (d *Downloader) DownloadFile(ctx context.Context, request *GetObjectRequest, filePath string, optFns ...func(*DownloaderOptions)) (result *DownloadResult, err error)
参数列表:
参数名 | 类型 | 说明 |
---|---|---|
ctx | context.Context | 请求的上下文 |
request | *GetObjectRequest | 下载对象的请求参数,和 GetObject 接口的 请求参数一致 |
filePath | string | 本地文件路径 |
optFns | ...func(*DownloaderOptions) | (可选),配置选项 |
DownloaderOptions选项说明:
参数 | 类型 | 说明 |
---|---|---|
PartSize | int64 | 指定分片大小,默认值为 6MiB |
ParallelNum | int | 指定上传任务的并发数,默认值为 3。针对的是单次调用的并发限制,而不是全局的并发限制 |
EnableCheckpoint | bool | 是否记录断点下载信息,默认不记录 |
CheckpointDir | string | 指定记录文件的保存路径,例如 /local/dir/, 当EnableCheckpoint 为 true时有效 |
VerifyData | bool | 恢复下载时,是否要校验已下载数据的CRC64值,默认不校验, 当EnableCheckpoint 为 true时有效 |
UseTempFile | bool | 下载文件时,是否使用临时文件,默认使用。先下载到 临时文件上,当成功后,再重命名为目标文件 |
当使用NewDownloader实例化实例时,您可以指定多个配置选项来自定义对象的下载行为。也可以在每次调用下载接口时,指定多个配置选项来自定义每次下载对象的行为。
设置Downloader的配置参数
d := client.NewDownloader(func(do *oss.DownloaderOptions) {
do.PartSize = 10 * 1024 * 1024
})
设置每次上传请求的配置参数
request := &oss.GetObjectRequest{Bucket: oss.Ptr("bucket"), Key: oss.Ptr("key")}
d.DownloadFile(context.TODO(), request, "/local/dir/example", func(do *oss.DownloaderOptions) {
do.PartSize = 10 * 1024 * 1024
})
示例
- 使用 Downloader 下载到本地文件
...
client := oss.NewClient(cfg)
d := client.NewDownloader()
d.DownloadFile(context.TODO(),
&oss.GetObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
},
"/local/dir/example",
)
当需要将对象从存储空间复制到另外一个存储空间,或者修改对象的属性时,您可以通过拷贝接口 或者分片拷贝接口来完成这个操作。
这两个接口有其适用的场景,例如:
- 拷贝接口(CopyObject) 只适合拷贝 5GiB 以下的对象;
- 分片拷贝接口(UploadPartCopy) 不支持 元数据指令(x-oss-metadata-directive) 和 标签指令(x-oss-tagging-directive) 参数, 拷贝时,您需要主动去设置需要复制的元数据和标签。
- 服务端优化了拷贝(CopyObject)接口,使其具备浅拷贝的能力,在特定的场景下也支持拷贝大文件。
拷贝管理器提供了通用的拷贝接口,隐藏了接口的差异和实现细节,根据拷贝的请求参数,自动选择合适的接口复制对象。
type Copier struct {
...
}
func (c *Client) NewCopier(optFns ...func(*CopierOptions)) *Copier
func (c *Copier) Copy(ctx context.Context, request *CopyObjectRequest, optFns ...func(*CopierOptions)) (*CopyResult, error)
参数列表:
参数名 | 类型 | 说明 |
---|---|---|
ctx | context.Context | 请求的上下文 |
request | *CopyObjectRequest | 拷贝对象的请求参数,和 CopyObject 接口的 请求参数一致 |
optFns | ...func(*CopierOptions) | (可选),配置选项 |
CopierOptions选项说明:
参数 | 类型 | 说明 |
---|---|---|
PartSize | int64 | 指定分片大小,默认值为 64MiB |
ParallelNum | int | 指定上传任务的并发数,默认值为 3。针对的是单次调用的并发限制,而不是全局的并发限制 |
MultipartCopyThreshold | int64 | 使用分片拷贝的阈值,默认值为 200MiB |
LeavePartsOnError | bool | 当拷贝失败时,是否保留已拷贝的分片,默认不保留 |
DisableShallowCopy | bool | 不使用浅拷贝行为,默认使用 |
当使用NewCopier实例化实例时,您可以指定多个配置选项来自定义对象的下载行为。也可以在每次调用下载接口时,指定多个配置选项来自定义每次下载对象的行为。
设置Copier的配置参数
d := client.NewCopier(func(co *oss.CopierOptions) {
co.PartSize = 100 * 1024 * 1024
})
设置每次拷贝请求的配置参数
request := &oss.CopyObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
SourceBucket: oss.Ptr("src-bucket"),
SourceKey: oss.Ptr("src-key"),
}
copier.Copy(context.TODO(), request, func(co *oss.CopierOptions) {
co.PartSize = 100 * 1024 * 1024
})
注意:
拷贝对象时,CopyObjectRequest.MetadataDirective 决定了对象元数据的拷贝行为,默认 复制 源对象标签
拷贝对象时,CopyObjectRequest.TaggingDirective 决定了对象标签的拷贝行为,默认 复制 源对象标签
示例
- 拷贝文件,默认会复制 元数据 和 标签
...
client := oss.NewClient(cfg)
copier := client.NewCopier()
result, err := copier.Copy(context.TODO(), &oss.CopyObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
SourceBucket: oss.Ptr("src-bucket"),
SourceKey: oss.Ptr("src-key"),
})
if err != nil {
log.Fatalf("failed to UploadFile %v", err)
}
fmt.Printf("copy done, etag %v\n", oss.ToString(result.ETag))
- 拷贝文件,只拷贝数据,不拷贝元数据和标签
...
client := oss.NewClient(cfg)
copier := client.NewCopier()
result, err := copier.Copy(context.TODO(), &oss.CopyObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
SourceBucket: oss.Ptr("src-bucket"),
SourceKey: oss.Ptr("src-key"),
MetadataDirective: oss.Ptr("Replace"),
TaggingDirective: oss.Ptr("Replace"),
})
if err != nil {
log.Fatalf("failed to UploadFile %v", err)
}
fmt.Printf("copy done, etag %v\n", oss.ToString(result.ETag))
- 修改 对象的存储类型 为标准类型
...
client := oss.NewClient(cfg)
copier := client.NewCopier()
result, err := copier.Copy(context.TODO(), &oss.CopyObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
SourceBucket: oss.Ptr("src-bucket"),
SourceKey: oss.Ptr("src-key"),
StorageClass: oss.StorageClassStandard,
})
if err != nil {
log.Fatalf("failed to UploadFile %v", err)
}
fmt.Printf("copy done, etag %v\n", oss.ToString(result.ETag))
新增了File-Like接口,提供了模仿文件的读写行为来操作存储空间里的对象。
支持以下两种方式:
- 只读(ReadOnlyFile)
- 追加写(AppendOnlyFile)
以只读方式访问存储空间的对象。在只读方式上,提供了 单流 和 并发+预取 两种模式,您可以根据场景需要,调整并发数,以提升读的速度。同时,内部实现了连接断掉重连的机制,在一些比较复杂的网络环境下,具备更好的鲁棒性。
type ReadOnlyFile struct {
...
}
func (c *Client) OpenFile(ctx context.Context, bucket string, key string, optFns ...func(*OpenOptions)) (file *ReadOnlyFile, err error)
参数列表:
参数名 | 类型 | 说明 |
---|---|---|
ctx | context.Context | 请求的上下文 |
bucket | string | 设置存储空间名字 |
key | string | 设置对象名 |
optFns | ...func(*OpenOptions) | (可选),打开文件时的配置选项 |
返回值列表:
返回值名 | 类型 | 说明 |
---|---|---|
file | *ReadOnlyFile | 只读文件的实例,当 err 为nil 时有效 |
err | error | 打开只读文件的状态,当失败时,err 不为 nil |
OpenOptions选项说明:
参数 | 类型 | 说明 |
---|---|---|
Offset | int64 | 打开文件时的初始偏移量,默认值是0 |
VersionId | *string | 指定对象的版本号,多版本下有效 |
RequestPayer | *string | 启用了请求者付费模式时,需要设置为'requester' |
EnablePrefetch | bool | 是否启用预取模式,默认不启用 |
PrefetchNum | int | 预取块的数量,默认值为3。启用预取模式时有效 |
ChunkSize | int64 | 每个预取块的大小,默认值为6MiB。启用预取模式时有效 |
PrefetchThreshold | int64 | 持续顺序读取多少字节后进入到预取模式,默认值为20MiB。启用预取模式时有效 |
ReadOnlyFile接口:
接口名 | 说明 |
---|---|
Close() error | 关闭文件句柄,释放资源,例如内存,活动的socket 等 |
Read(p []byte) (int, error) | 从数据源中读取长度为len(p)的字节,存储到p中,返回读取的字节数和遇到的错误 |
Seek(offset int64, whence int) (int64, error) | 用于设置下一次读或写的偏移量。其中whence的取值:0:相对于头部,1:相对于当前偏移量,2:相对于尾部 |
Stat() (os.FileInfo, error) | 获取对象的信息,包括 对象大小,最后修改时间 以及元信息 |
注意: 当预取模式打开时,如果出现多次乱序读时,则会自动退回单流模式。
示例
- 以单流模式,读取整个对象
...
client := oss.NewClient(cfg)
f, err := client.OpenFile(context.TODO(), "bucket", "key")
if err != nil {
log.Fatalf("failed to open file %v", err)
}
defer f.Close()
written, err := io.Copy(io.Discard, f)
if err != nil {
log.Fatalf("failed to read file %v", err)
}
fmt.Print("read data count:%v", written)
- 启用预取模式,读取整个对象
...
client := oss.NewClient(cfg)
f, err := client.OpenFile(context.TODO(),
"bucket",
"key",
func(oo *oss.OpenOptions) {
oo.EnablePrefetch = true
})
if err != nil {
log.Fatalf("failed to open file %v", err)
}
defer f.Close()
written, err := io.Copy(io.Discard, f)
if err != nil {
log.Fatalf("failed to read file %v", err)
}
fmt.Print("read data count:%v", written)
- 通过Seek方法,从指定位置开始读取剩余的数据
...
client := oss.NewClient(cfg)
f, err := client.OpenFile(context.TODO(), "bucket", "key")
if err != nil {
log.Fatalf("failed to open file %v", err)
}
defer f.Close()
// 获取对象信息
info, _ := f.Stat()
// 基本属性
fmt.Printf("size:%v, mtime:%v\n", info.Size(), info.ModTime())
// 对象元数据
if header, ok := info.Sys().(http.Header); ok {
fmt.Printf("content-type:%v\n", header.Get(oss.HTTPHeaderContentType))
}
// 设置文件的偏移值,例如 从123开始
_, err = f.Seek(123, io.SeekStart)
if err != nil {
log.Fatalf("failed to seek file %v", err)
}
written, err := io.Copy(io.Discard, f)
if err != nil {
log.Fatalf("failed to read file %v", err)
}
fmt.Print("read data count:%v", written)
调用AppendObject接口以追加写的方式上传数据。如果对象不存在,则创建追加类型的对象。如果对象存在,并且不为追加类型的对象时,则返回错误。
type AppendOnlyFile struct {
...
}
func (c *Client) AppendFile(ctx context.Context, bucket string, key string, optFns ...func(*AppendOptions)) (*AppendOnlyFile, error)
参数列表:
参数名 | 类型 | 说明 |
---|---|---|
ctx | context.Context | 请求的上下文 |
bucket | string | 设置存储空间名字 |
key | string | 设置对象名 |
optFns | ...func(*AppendOptions) | (可选),追加文件时的配置选项 |
返回值列表:
返回值名 | 类型 | 说明 |
---|---|---|
file | *AppendOnlyFile | 追加文件的实例,当 err 为nil 时有效 |
err | error | 打开追加文件时的状态,当失败时,err 不为 nil |
AppendOptions选项说明:
参数 | 类型 | 说明 |
---|---|---|
RequestPayer | *string | 启用了请求者付费模式时,需要设置为'requester' |
CreateParameter | *AppendObjectRequest | 用于首次上传时,设置对象的元信息,包括ContentType,Metadata,权限,存储类型 等 |
AppendOnlyFile接口:
接口名 | 说明 |
---|---|
Close() error | 关闭文件句柄,释放资源 |
Write(b []byte) (int, error) | 将b中的数据写入到数据流中,返回写入的字节数和遇到的错误 |
WriteFrom(r io.Reader) (int64, error) | 将r中的数据写入到数据流中,返回写入的字节数和遇到的错误 |
Stat() (os.FileInfo, error) | 获取对象的信息,包括 对象大小,最后修改时间 以及元信息 |
示例
- 把多个本地文件合并成一个文件
...
client := oss.NewClient(cfg)
f, err := client.AppendFile(context.TODO(), "bucket", "key")
if err != nil {
log.Fatalf("failed to append file %v", err)
}
defer f.Close()
// example1.txt
lf, err := os.Open("/local/dir/example1.txt")
if err != nil {
log.Fatalf("failed to local file %v", err)
}
_, err = f.WriteFrom(lf)
if err != nil {
log.Fatalf("failed to append file %v", err)
}
lf.Close()
// example2.txt
lf, err = os.Open("/local/dir/example2.txt")
if err != nil {
log.Fatalf("failed to local file %v", err)
}
_, err = f.WriteFrom(lf)
if err != nil {
log.Fatalf("failed to append file %v", err)
}
lf.Close()
// example3.txt
lb, err := os.ReadFile("/local/dir/example3.txt")
_, err = f.Write(lb)
if err != nil {
log.Fatalf("failed to append file %v", err)
}
- 合并数据时,同时设置对象的权限和存储类型
...
client := oss.NewClient(cfg)
f, err := client.AppendFile(context.TODO(),
"bucket",
"key",
func(ao *oss.AppendOptions) {
ao.CreateParameter = &oss.AppendObjectRequest{
Acl: oss.ObjectACLPrivate,
Metadata: map[string]string{
"user": "jack",
},
Tagging: oss.Ptr("key=value"),
}
},
)
if err != nil {
log.Fatalf("failed to append file %v", err)
}
defer f.Close()
_, err = f.Write([]byte("hello"))
if err != nil {
log.Fatalf("failed to append file %v", err)
}
_, err = f.Write([]byte("world"))
if err != nil {
log.Fatalf("failed to append file %v", err)
}
info, err := f.Stat()
if err != nil {
log.Fatalf("failed to stat file %v", err)
}
fmt.Printf("size:%v, mtime:%v\n", info.Size(), info.ModTime())
if header, ok := info.Sys().(http.Header); ok {
fmt.Printf("user:%v\n", header.Get("x-oss-meta-user"))
}
客户端加密是在数据上传至OSS之前,由用户在本地对数据进行加密处理,确保只有密钥持有者才能解密数据,增强数据在传输和存储过程中的安全性。
注意:
使用客户端加密功能时,您需要对主密钥的完整性和正确性负责。
在对加密数据进行复制或者迁移时,您需要对加密元数据的完整性和正确性负责。
如果您需要了解OSS客户端加密实现的原理,请参考OSS用户指南中的客户端加密。
使用客户端加密,首先您需要实例化加密客户端,然后调用其提供的接口进行操作。您的对象将作为请求的一部分自动加密和解密。
type EncryptionClient struct {
...
}
func NewEncryptionClient(c *Client, masterCipher crypto.MasterCipher, optFns ...func(*EncryptionClientOptions)) (eclient *EncryptionClient, err error)
参数列表:
参数名 | 类型 | 说明 |
---|---|---|
c | *Client | 非加密客户端实例 |
masterCipher | crypto.MasterCipher | 主密钥实例,用于加密和解密数据密钥 |
optFns | ...func(*EncryptionClientOptions) | (可选),加密客户端配置选项 |
返回值列表:
返回值名 | 类型 | 说明 |
---|---|---|
eclient | *EncryptionClient | 加密客户端实例, 当 err 为 nil 时有效 |
err | error | 创建加密客户端的状态,当失败时,err 不为 nil |
EncryptionClientOptions选项说明:
参数 | 类型 | 说明 |
---|---|---|
MasterCiphers | []crypto.MasterCipher | 主密钥实例组, 用于解密数据密钥。 |
EncryptionClient接口:
基础接口名 | 说明 |
---|---|
GetObjectMeta | 获取对象的部分元信息 |
HeadObject | 获取对象的部元信息 |
GetObject | 下载对象,并自动解密 |
PutObject | 上传对象,并自动加密 |
InitiateMultipartUpload | 初始化一个分片上传事件 和 分片加密上下文(EncryptionMultiPartContext) |
UploadPart | 初始化一个分片上传事件, 调用该接口上传分片数据,并自动加密。调用该接口时,需要设置 分片加密上下文 |
CompleteMultipartUpload | 在将所有分片数据上传完成后,调用该接口合并成一个文件 |
AbortMultipartUpload | 取消分片上传事件,并删除对应的分片数据 |
ListParts | 列举指定上传事件所属的所有已经上传成功分片 |
高级接口名 | 说明 |
NewDownloader | 创建下载管理器实例 |
NewUploader | 创建上传管理器实例 |
OpenFile | 创建ReadOnlyFile实例 |
辅助接口名 | 说明 |
Unwrap | 获取非加密客户端实例,可以通过该实例访问其它基础接口 |
说明: EncryptionClient 采用了 和 Client 一样的接口命名规则 和 调用方式,有关接口的详细用法,请参考指南的其它章节说明。
创建RAS加密客户端
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/crypto"
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion("your region")
client := oss.NewClient(cfg)
// 创建一个主密钥的描述信息,创建后不允许修改。主密钥描述信息和主密钥一一对应。
// 如果所有的对象都使用相同的主密钥,主密钥描述信息可以为空,但后续不支持更换主密钥。
// 如果主密钥描述信息为空,解密时无法判断使用的是哪个主密钥。
// 强烈建议为每个主密钥都配置主密钥描述信息,由客户端保存主密钥和描述信息之间的对应关系。
materialDesc := make(map[string]string)
materialDesc["desc"] = "your master encrypt key material describe information"
// 创建只包含 主密钥 的 加密客户端
mc, err := crypto.CreateMasterRsa(materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey")
eclient, err := NewEncryptionClient(client, mc)
// 创建包含主密钥 和 多个解密密钥的 加密客户端
// 当解密时,先匹配解密密钥的描述信息,如果不匹配,则使用主密钥解密
//decryptMC := []crypto.MasterCipher{
// // TODO
//}
//eclient, err := oss.NewEncryptionClient(client, mc, func(eco *oss.EncryptionClientOptions) {
// eco.MasterCiphers = decryptMC
//})
使用加密客户端上传或者下载
...
eclient, err := NewEncryptionClient(client, mc)
// Use PutObject
_, err = eclient.PutObject(context.TODO(), &oss.PutObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
Body: bytes.NewReader([]byte("hello world")),
})
if err != nil {
log.Fatalf("failed to PutObject %v", err)
}
// Use GetObject
gresult, err := eclient.GetObject(context.TODO(), &oss.GetObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
})
if err != nil {
log.Fatalf("failed to GetObject %v", err)
}
io.Copy(io.Discard, gresult.Body)
gresult.Body.Close()
// Use Downloader
d := eclient.NewDownloader()
_, err = d.DownloadFile(context.TODO(),
&oss.GetObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
},
"/local/dir/example",
)
if err != nil {
log.Fatalf("failed to DownloadFile %v", err)
}
// Use Uploader
u := eclient.NewUploader()
_, err = u.UploadFile(context.TODO(),
&oss.PutObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
},
"/local/dir/example",
)
if err != nil {
log.Fatalf("failed to UploadFile %v", err)
}
// Use ReadOnlyFile
f, err := eclient.OpenFile(context.TODO(), "bucket", "key")
if err != nil {
log.Fatalf("failed to OpenFile %v", err)
}
defer f.Close()
_, err = io.Copy(io.Discard, f)
if err != nil {
log.Fatalf("failed to Copy %v", err)
}
使用加密客户端以分片方式上传数据
以上传500K内存数据为例
...
eclient, err := NewEncryptionClient(client, mc)
var (
bucketName string = "bucket"
objectName string = "key"
length = int64(500 * 1024)
partSize = int64(200 * 1024)
partsNum = int(length/partSize + 1)
data = make([]byte, length, length)
)
// 加密客户端 需要 设置分片大小和总文件大小
initResult, err := eclient.InitiateMultipartUpload(context.TODO(), &oss.InitiateMultipartUploadRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
CSEPartSize: oss.Ptr(partSize),
CSEDataSize: oss.Ptr(length),
})
var parts oss.UploadParts
for i := 0; i < partsNum; i++ {
start := int64(i) * partSize
end := start + partSize
if end > length {
end = length
}
// 加密客户端 需要 设置分片加密上下文
upResult, err := eclient.UploadPart(context.TODO(), &oss.UploadPartRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
UploadId: initResult.UploadId,
PartNumber: int32(i + 1),
CSEMultiPartContext: initResult.CSEMultiPartContext,
Body: bytes.NewReader(data[start:end]),
})
if err != nil {
log.Fatalf("failed to UploadPart %v", err)
}
parts = append(parts, oss.UploadPart{PartNumber: int32(i + 1), ETag: upResult.ETag})
}
sort.Sort(parts)
_, err = eclient.CompleteMultipartUpload(context.TODO(), &oss.CompleteMultipartUploadRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
UploadId: initResult.UploadId,
CompleteMultipartUpload: &oss.CompleteMultipartUpload{Parts: parts},
})
if err != nil {
log.Fatalf("failed to CompleteMultipartUpload %v", err)
}
当RSA主密钥方式无法满足需求时,您可自定主密钥的加密实现。主密钥的接口定义如下:
type MasterCipher interface {
Encrypt([]byte) ([]byte, error)
Decrypt([]byte) ([]byte, error)
GetWrapAlgorithm() string
GetMatDesc() string
}
MasterCipher接口说明
接口名 | 说明 |
---|---|
Encrypt | 加密 数据加密密钥 和 加密数据的初始值(IV) |
Decrypt | 解密 数据加密密钥 和 加密数据的初始值(IV) |
GetWrapAlgorithm | 返回 数据密钥的加密算法信息,建议采用 算法/模式/填充 格式,例如RSA/NONE/PKCS1Padding |
GetMatDesc | 返回 主密钥的描述信息,JSON格式 |
例如
...
type MasterCustomCipher struct {
MatDesc string
SecrectKey string
}
func (mrc MasterCustomCipher) GetWrapAlgorithm() string {
return "Custom/None/NoPadding"
}
func (mrc MasterCustomCipher) GetMatDesc() string {
return mrc.MatDesc
}
func (mrc MasterCustomCipher) Encrypt(plainData []byte) ([]byte, error) {
// TODO
}
func (mrc MasterCustomCipher) Decrypt(cryptoData []byte) ([]byte, error) {
// TODO
}
func MasterCustomCipher(matDesc map[string]string, secrectKey string) (crypto.MasterCipher, error) {
var jsonDesc string
if len(matDesc) > 0 {
b, err := json.Marshal(matDesc)
if err != nil {
return nil, err
}
jsonDesc = string(b)
}
return MasterCustomCipher{MatDesc: jsonDesc, SecrectKey: secrectKey}, nil
}
client := oss.NewClient(cfg)
materialDesc := make(map[string]string)
materialDesc["desc"] = "your master encrypt key material describe information"
mc, err := MasterCustomCipher(materialDesc, "yourSecrectKey")
eclient, err := NewEncryptionClient(client, mc)
为了方便用户使用,封装了一些易用性接口。当前扩展的接口如下:
接口名 | 说明 |
---|---|
IsObjectExist | 判断对象(object)是否存在 |
IsBucketExist | 判断存储空间(bucket)是否存在 |
PutObjectFromFile | 上传本地文件到存储空间 |
GetObjectToFile | 下载对象到本地文件 |
这两个接口的返回值为 (bool, error), 当 error 为 nil时,如果bool 为 true,表示存在,如果 bool值为 false,表示不存在。当 error 不为 nil时,表示无法从该错误信息判断 是否存在。
func (c *Client) IsObjectExist(ctx context.Context, bucket string, key string, optFns ...func(*IsObjectExistOptions)) (bool, error)
func (c *Client) IsBucketExist(ctx context.Context, bucket string, optFns ...func(*Options)) (bool, error)
例如 判断对象是否存在
client := oss.NewClient(cfg)
existed, err := client.IsObjectExist(context.TODO(), "examplebucket", "exampleobject")
//existed, err := client.IsObjectExist(context.TODO(), "examplebucket", "exampleobject", func(ioeo *oss.IsObjectExistOptions) {
// //ioeo.VersionId = oss.Ptr("versionId")
// //ioeo.RequestPayer = oss.Ptr("requester")
//})
if err != nil {
// Error
} else {
fmt.Printf("object existed :%v", existed)
}
使用简单上传(PutObject)接口 把本地文件上传到存储空间,该接口不支持并发。
func (c *Client) PutObjectFromFile(ctx context.Context, request *PutObjectRequest, filePath string, optFns ...func(*Options)) (*PutObjectResult, error)
示例
client := oss.NewClient(cfg)
result, err := client.PutObjectFromFile(context.TODO(),
&oss.PutObjectRequest{
Bucket: oss.Ptr("examplebucket"),
Key: oss.Ptr("exampleobject"),
},
"/local/dir/example",
)
使用GetObject接口,把存储空间的对象下载到本地文件,该接口不支持并发。
func (c *Client) GetObjectToFile(ctx context.Context, request *GetObjectRequest, filePath string, optFns ...func(*Options)) (*GetObjectResult, error)
示例
client := oss.NewClient(cfg)
result, err := client.GetObjectToFile(context.TODO(),
&oss.GetObjectRequest{
Bucket: oss.Ptr("examplebucket"),
Key: oss.Ptr("exampleobject"),
},
"/local/dir/example",
)
提供了各种上传下载接口,您可以根据使用场景,选择适合的接口。
上传接口
接口名 | 说明 |
---|---|
Client.PutObject | 简单上传, 最大支持5GiB 支持CRC64数据校验(默认启用) 支持进度条 请求body类型为io.Reader, 当支持io.Seeker类型时,具备失败重传 |
Client.PutObjectFromFile | 与Client.PutObject接口能力一致 请求body数据来源于文件路径 |
分片上传接口 Client.InitiateMultipartUpload Client.UploadPar Client.CompleteMultipartUpload |
分片上传,单个分片最大5GiB,文件最大48.8TiB UploadPart接口支持CRC64校验(默认启用) UploadPart接口支持进度条 UploadPart>请求body类型为io.Reader, 当支持io.Seeker类型时,具备失败重传 |
Uploader.UploadFrom | 封装了简单上传 和 分片上传接口,最大支持48.8TiB 支持CRC64数据校验(默认启用) 支持进度条 请求body参数类型为io.Reader,当同时支持 io.Reader, io.Seeker 和 io.ReaderAt 类型时,不需要把数据缓存在内存里,否则 必须先把数据缓冲在内存中,然后才能上传该部分 |
Uploader.UploadFile | 与Uploader.UploadFrom接口能力一致 请求body数据来源于文件路径 支持断点续传 |
Client.AppendObject | 追加上传, 最终文件最大支持5GiB 支持CRC64数据校验(默认启用) 支持进度条 请求body类型为io.Reader, 当支持io.Seeker类型时,具备失败重传(该接口为非幂等接口,重传时可能出现失败) |
AppendOnlyFile接口 AppendOnlyFile.Write AppendOnlyFile.WriteFrom |
与Client.AppendObject接口能力一致 优化了重传时失败后容错处理 |
下载接口
接口名 | 说明 |
---|---|
Client.GetObject | 流式下载, 响应体为io.ReadCloser类型 不直接支持CRC64校验 不直接支持进度条 流式读数据阶段,不支持失败重连 |
Client.GetObjectToFile | 下载到本地文件 单连接下载 支持CRC64数据校验(默认启用) 支持进度条 支持失败重连 |
Downloader.DownloadFile | 采用分片方式下载到本地文件 支持自定义分片大小和并发数 支持CRC64数据校验(默认启用) 支持进度条 支持失败重连 支持断点续传 先写临时文件,再重命名(可配置,默认启用) |
ReadOnlyFile接口 ReadOnlyFile.Read ReadOnlyFile.Seek ReadOnlyFile.Close |
File-Like形式接口, 提供io.Reader, io.Seeker 和 io.Closer接口 具备Seek能力 支持单流模式(默认) 支持异步预取模式,提升读的速度 支持自定义预取块和预取数 不直接支持CRC64校验 不直接支持进度条 支持失败重连 |
本部分将从使用场景出发, 介绍如何使用SDK。
包含的主题
在对象的上传,下载 和 拷贝 场景下,您可以设置进度条,用于查看对象的传输状态。
支持设置进度条的请求参数
支持的请求参数 | 用法 |
---|---|
PutObjectRequest | PutObjectRequest.ProgressFunc |
GetObjectRequest | GetObjectRequest.ProgressFunc |
CopyObjectRequest | CopyObjectRequest.ProgressFunc |
AppendObjectRequest | AppendObjectRequest.ProgressFunc |
UploadPartRequest | UploadPartRequest.ProgressFunc |
ProgressFunc定义和参数说明
type ProgressFunc func(increment, transferred, total int64)
参数名 | 类型 | 说明 |
---|---|---|
increment | int64 | 本次回调传输的数据大小,单位字节 |
transferred | int64 | 已传输的数据大小,单位为字节 |
total | int64 | 本次请求的数据大小,单位为字节,如果为 -1,表示获无法获取总大小 |
示例
- 上传时,设置进度条,以GetObject 为例
...
client := oss.NewClient(cfg)
client.PutObject(context.TODO(), &oss.PutObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
ProgressFn: func(increment, transferred, total int64) {
fmt.Printf("increment:%v, transferred:%v, total:%v\n", increment, transferred, total)
},
})
- 下载时,设置进度条,以GetObjectToFile为例
...
client := oss.NewClient(cfg)
client.GetObjectToFile(context.TODO(),
&oss.GetObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
ProgressFn: func(increment, transferred, total int64) {
fmt.Printf("increment:%v, transferred:%v, total:%v\n", increment, transferred, total)
},
},
"/local/dir/example",
)
- 流式下载时,设置进度条,以GetObject 为例
...
client := oss.NewClient(cfg)
result, err := client.GetObject(context.TODO(), &oss.GetObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
})
if err != nil {
log.Fatalf("fail to GetObject %v", err)
}
prop := oss.NewProgress(
func(increment, transferred, total int64) {
fmt.Printf("increment:%v, transferred:%v, total:%v\n", increment, transferred, total)
},
result.ContentLength,
)
io.ReadAll(io.TeeReader(result.Body, prop))
OSS提供基于MD5和CRC64的数据校验,确保请求的过程中的数据完整性。
当向OSS发送请求时,如果设置了Content-MD5,OSS会根据接收的内容计算MD5。当OSS计算的MD5值和上传提供的MD5值不一致时,则返回InvalidDigest异常,从而保证数据的完整性。
基础接口里,除了 PutObject, AppendObject, UploadPart 接口外,会自动计算MD5, 并设置Content-MD5, 保证请求的完整性。
如果您需要在 PutObject, AppendObject, UploadPart 接口里使用MD5校验,可以参考以下写法
...
client := oss.NewClient(cfg)
var body io.Reader
// 计算Content-Md5, 如果 body 不是 io.ReadSeeker 类型, 则先读到缓存里,再计算MD5
calcMd5 := func(input io.Reader) (io.Reader, string, error) {
if input == nil {
return input, "1B2M2Y8AsgTpgAmY7PhCfg==", nil
}
var (
r io.ReadSeeker
ok bool
)
if r, ok = input.(io.ReadSeeker); !ok {
buf, err := io.ReadAll(input)
if err != nil {
return input, "", err
}
r = bytes.NewReader(buf)
}
curPos, err := r.Seek(0, io.SeekCurrent)
if err != nil {
return input, "", err
}
h := md5.New()
_, err = io.Copy(h, r)
if err != nil {
return input, "", err
}
_, err = r.Seek(curPos, io.SeekStart)
if err != nil {
return input, "", err
}
return r, base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
}
body, md5, err := calcMd5(body)
if err != nil {
log.Fatalf("fail to calcMd5, %v", err)
}
result, err := client.PutObject(context.TODO(), &oss.PutObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
ContentMD5: oss.Ptr(md5),
Body: body,
})
if err != nil {
log.Fatalf("fail to PutObject, %v", err)
}
fmt.Printf("PutObject result, etg:%v", oss.ToString(result.ETag))
上传对象时,默认开启CRC64数据校验,以确保数据的完整性,例如 PutObject, AppendObject, UploadPart 等接口。
下载对象时,
- 如果是下载到本地文件,默认开启CRC64数据校验,以确保数据的完整性,例如 Downloader.DownloadFile 和 GetObjectToFile 接口。
- 如果是流式读类型的接口,不会做CRC64校验,例如 GetObject 和 ReadOnlyFile.Read 接口。
如果您需要在流式读接口里使用CRC64校验,可以参考以下写法
...
client := oss.NewClient(cfg)
result, err := client.GetObject(context.TODO(), &oss.GetObjectRequest{
Bucket: oss.Ptr("bucket"),
Key: oss.Ptr("key"),
})
if err != nil {
log.Fatalf("fail to GetObject, %v", err)
}
defer func() {
if result.Body != nil {
result.Body.Close()
}
}()
var h hash.Hash64
var r io.Reader = result.Body
// 响应头返回的是整个文件的CRC64值,如果是范围下载,不支持CRC64校验
// 206 Partial Content 表示是范围下载
if result.StatusCode == 200 {
h = oss.NewCRC64(0)
r = io.TeeReader(result.Body, h)
}
_, err = io.Copy(io.Discard, r)
if err != nil {
log.Fatalf("fail to Copy, %v", err)
}
if h != nil && result.HashCRC64 != nil {
ccrc := fmt.Sprint(h.Sum64())
scrc := oss.ToString(result.HashCRC64)
if ccrc != scrc {
log.Fatalf("crc is inconsistent, client %s, server %s", ccrc, scrc)
}
}
如果您需要关闭CRC64校验,通过Config.WithDisableDownloadCRC64Check 和 Config.WithDisableUploadCRC64Check 配置,例如
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region).
WithDisableDownloadCRC64Check(true).
WithDisableUploadCRC64Check(true)
client := oss.NewClient(cfg)
本部分介绍如何从V1 版本(aliyun-oss-go-sdk) 迁移到 V2 版本。
V2 版本 要求 Go 版本最低为 1.18。
V2 版本使用新的代码仓库,同时也对代码结构进行了调整,按照功能模块组织,以下是这些模块路径和说明:
模块路径 | 说明 |
---|---|
github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss | SDK核心,接口 和 高级接口实现 |
github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials | 访问凭证相关 |
github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/retry | 重试相关 |
github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/signer | 签名相关 |
github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/transport | HTTP客户端相关 |
github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/crypto | 客户端加密相关 |
示例
// v1
import (
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
// v2
import (
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
// 根据需要,导入 retry,transport 或者 signer
//"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/xxxx"
)
V2 版本简化了配置设置方式,全部迁移到 config 下,并提供了以With为前缀的辅助函数,方便以编程方式覆盖缺省配置。
V2 默认使用 V4签名,所以必须配置区域(Region)。
V2 支持从区域(Region)信息构造 访问域名(Endpoint), 当访问的是公有云时,可以不设置Endpoint。
示例
// v1
import (
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
...
// 环境变量中获取访问凭证
provider, err := oss.NewEnvironmentVariableCredentialsProvider()
// 设置HTTP连接超时时间为20秒,HTTP读取或写入超时时间为60秒。
time := oss.Timeout(20,60)
// 不校验SSL证书校验
verifySsl := oss.InsecureSkipVerify(true)
// 设置日志
logLevel := oss.SetLogLevel(oss.LogInfo)
// Endpoint
endpoint := "oss-cn-hangzhou.aliyuncs.com"
client, err := oss.New(endpoint, "", "", oss.SetCredentialsProvider(&provider), time, verifySsl, logLevel)
// v2
import (
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
...
// 环境变量中获取访问凭证
provider := credentials.NewEnvironmentVariableCredentialsProvider()
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(provider).
// 设置HTTP连接超时时间为20秒
WithConnectTimeout(20 * time.Second).
// HTTP读取或写入超时时间为60秒
ReadWriteTimeout(60 * time.Second).
// 不校验SSL证书校验
WithInsecureSkipVerify(true).
// 设置日志
WithLogLevel(oss.LogInfo).
// 设置区域
WithRegion("cn-hangzhou")
client := oss.NewClient(cfg)
V2 版本 把 Client 的创建 函数 从 New 修改 为 NewClient, 同时 创建函数 不在支持传入Endpoint 以及 access key id 和 access key secrect 参数。
示例
// v1
client, err := oss.New(endpoint, "ak", "sk")
// v2
client := oss.NewClient(cfg)
基础 API 接口 都 合并为 单一操作方法 '<OperationName>',操作的请求参数为 '<OperationName>Request',操作的返回值为 '<OperationName>Result'。这些操作方法都 迁移到 Client下,同时需要设置 context.Context。如下格式:
func (c *Client) <OperationName>(ctx context.Context, request *<OperationName>Request, optFns ...func(*Options)) (*<OperationName>Result,, error)
关于API接口的详细使用说明,请参考基础接口。
示例
// v1
import "github.com/aliyun/aliyun-oss-go-sdk/oss"
provider, err := oss.NewEnvironmentVariableCredentialsProvider()
client, err := oss.New("yourEndpoint", "", "", oss.SetCredentialsProvider(&provider))
bucket, err := client.Bucket("examplebucket")
err = bucket.PutObject("exampleobject.txt", bytes.NewReader([]byte("example data")))
// v2
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion("your region")
client := oss.NewClient(cfg)
result, err := client.PutObject(context.TODO(), &oss.PutObjectRequest{
Bucket: oss.Ptr("examplebucket"),
Key: oss.Ptr("exampleobject.txt"),
Body: bytes.NewReader([]byte("example data")),
})
V2 版本 把 预签名接口 名字从 SignURL 修改为 Presign,同时把 接口 迁移到 Client 下。接口形式如下:
func (c *Client) Presign(ctx context.Context, request any, optFns ...func(*PresignOptions)) (*PresignResult, error)
对于 request 参数,其类型 与 API 接口中的 '<OperationName>Request' 一致。
对于返回结果,除了返回 预签名 URL 外,还返回 HTTP 方法,过期时间 和 被签名的请求头,如下:
type PresignResult struct {
Method string
URL string
Expiration time.Time
SignedHeaders map[string]string
}
关于预签名的详细使用说明,请参考预签名接口。
以 生成下载对象的预签名URL 为例,如何从 V1 迁移到 V2
// v1
import "github.com/aliyun/aliyun-oss-go-sdk/oss"
provider, err := oss.NewEnvironmentVariableCredentialsProvider()
client, err := oss.New("yourEndpoint", "", "", oss.SetCredentialsProvider(&provider))
bucket, err := client.Bucket("examplebucket")
signedURL, err := bucket.SignURL("exampleobject.txt", oss.HTTPGet, 60)
fmt.Printf("Sign Url:%s\n", signedURL)
// v2
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion("your region")
client := oss.NewClient(cfg)
result, err := client.Presign(
context.TODO(),
&oss.GetObjectRequest{
Bucket: oss.Ptr("examplebucket"),
Key: oss.Ptr("exampleobject.txt"),
},
oss.PresignExpires(60*time.Second),
)
fmt.Printf("Sign Method:%v\n", result.Method)
fmt.Printf("Sign Url:%v\n", result.URL)
fmt.Printf("Sign Expiration:%v\n", result.Expiration)
for k, v := range result.SignedHeaders {
fmt.Printf("SignedHeader %v:%v\n", k, v)
}
V2 版本使用 传输管理器 'Uploader','Downloader' 和 'Copier' 分别 管理 对象的 上传,下载 和 拷贝。 同时移除了原有的 断点续传接口 Bucket.UploadFile,Bucket.DownloadFile 和 Bucket.CopyFile。
接口对比如下:
场景 | v2 | v1 |
---|---|---|
上传文件 | Uploader.UploadFile | Bucket.UploadFile |
上传流(io.Reader) | Uploader.UploadFrom | 不支持 |
下载到文件 | Downloader.DownloadFile | Bucket.DownloadFile |
拷贝对象 | Copier.Copy | Bucket.CopyFile |
默认参数的变化
场景 | v2 | v1 |
---|---|---|
上传-分片默认值 | 6 MiB | 通过参数设置 |
上传-并发默认值 | 3 | 1 |
上传-阈值 | 分片大小 | 无 |
上传-记录checkpoint | 支持 | 支持 |
下载-分片默认值 | 6 MiB | 通过参数设置 |
下载-并发默认值 | 3 | 1 |
下载-阈值 | 分片大小 | 无 |
下载-记录checkpoint | 支持 | 支持 |
拷贝-分片默认值 | 64 MiB | Bucket.UploadFile |
拷贝-并发默认值 | 3 | 1 |
拷贝-阈值 | 200 MiB | 无 |
拷贝-记录checkpoint | 不支持 | 支持 |
阈值(上传/下载拷贝) 表示 对象/文件 大小 大于该值时,使用分片方式(上传/下载/拷贝)。
关于传输管理器的详细使用说明,请参考传输管理器。
V2 版本 使用 EncryptionClient 来提供 客户端加密功能,同时也对API 接口做了精简,采用了 和 Client 一样的接口命名规则 和 调用方式。
另外,该版本 仅保留 基于 RSA 自主管理的主密钥 的参考实现。
对于 KSM 的实现,可以参考sample/crypto/kms.go。
关于客户端加密的详细使用说明,请参考客户端加密。
以 使用主密钥RSA 上传对象为例,如何从 V1 迁移到 V2
// v1
import "github.com/aliyun/aliyun-oss-go-sdk/oss"
import "github.com/aliyun/aliyun-oss-go-sdk/oss/crypto"
provider, err := oss.NewEnvironmentVariableCredentialsProvider()
client, err := oss.New("yourEndpoint", "", "", oss.SetCredentialsProvider(&provider))
materialDesc := make(map[string]string)
materialDesc["desc"] = "your master encrypt key material describe information"
masterRsaCipher, err := osscrypto.CreateMasterRsa(materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey")
contentProvider := osscrypto.CreateAesCtrCipher(masterRsaCipher)
cryptoBucket, err := osscrypto.GetCryptoBucket(client, "examplebucket", contentProvider)
err = cryptoBucket.PutObject("exampleobject.txt", bytes.NewReader([]byte("example data")))
// v2
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
import "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/crypto"
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion("your region")
client := oss.NewClient(cfg)
materialDesc := make(map[string]string)
materialDesc["desc"] = "your master encrypt key material describe information"
mc, err := crypto.CreateMasterRsa(materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey")
eclient, err := NewEncryptionClient(client, mc)
result, err := eclient.PutObject(context.TODO(), &PutObjectRequest{
Bucket: Ptr("examplebucket"),
Key: Ptr("exampleobject.txt"),
Body: bytes.NewReader([]byte("example data")),
})
V2 版本 默认开启对HTTP请求的重试行为。从 V1 版本迁移到 V2 时,您需要移除原有的重试代码,避免放大重试次数。