|
|
package storage
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
"fmt"
|
|
|
"github.com/qiniu/go-sdk/v7/auth"
|
|
|
"github.com/qiniu/go-sdk/v7/internal/clientv2"
|
|
|
"github.com/qiniu/go-sdk/v7/internal/hostprovider"
|
|
|
"strings"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
// 存储所在的地区,例如华东,华南,华北
|
|
|
// 每个存储区域可能有多个机房信息,每个机房可能有多个上传入口
|
|
|
type Region struct {
|
|
|
// 上传入口
|
|
|
SrcUpHosts []string `json:"src_up,omitempty"`
|
|
|
|
|
|
// 加速上传入口
|
|
|
CdnUpHosts []string `json:"cdn_up,omitempty"`
|
|
|
|
|
|
// 获取文件信息入口
|
|
|
RsHost string `json:"rs,omitempty"`
|
|
|
|
|
|
// bucket列举入口
|
|
|
RsfHost string `json:"rsf,omitempty"`
|
|
|
|
|
|
ApiHost string `json:"api,omitempty"`
|
|
|
|
|
|
// 存储io 入口
|
|
|
IovipHost string `json:"io,omitempty"`
|
|
|
|
|
|
// 源站下载入口
|
|
|
IoSrcHost string `json:"io_src,omitempty"`
|
|
|
}
|
|
|
|
|
|
type RegionID string
|
|
|
|
|
|
// GetDefaultReion 根据RegionID获取对应的Region信息
|
|
|
func GetRegionByID(regionID RegionID) (Region, bool) {
|
|
|
if r, ok := regionMap[regionID]; ok {
|
|
|
return r, ok
|
|
|
}
|
|
|
return Region{}, false
|
|
|
}
|
|
|
|
|
|
func (r *Region) String() string {
|
|
|
str := ""
|
|
|
str += fmt.Sprintf("SrcUpHosts: %v\n", r.SrcUpHosts)
|
|
|
str += fmt.Sprintf("CdnUpHosts: %v\n", r.CdnUpHosts)
|
|
|
str += fmt.Sprintf("IovipHost: %s\n", r.IovipHost)
|
|
|
str += fmt.Sprintf("RsHost: %s\n", r.RsHost)
|
|
|
str += fmt.Sprintf("RsfHost: %s\n", r.RsfHost)
|
|
|
str += fmt.Sprintf("ApiHost: %s\n", r.ApiHost)
|
|
|
return str
|
|
|
}
|
|
|
|
|
|
func endpoint(useHttps bool, host string) string {
|
|
|
host = strings.TrimSpace(host)
|
|
|
if host == "" {
|
|
|
return ""
|
|
|
}
|
|
|
|
|
|
if strings.HasPrefix(host, "http://") ||
|
|
|
strings.HasPrefix(host, "https://") {
|
|
|
return host
|
|
|
}
|
|
|
|
|
|
scheme := "http://"
|
|
|
if useHttps {
|
|
|
scheme = "https://"
|
|
|
}
|
|
|
return fmt.Sprintf("%s%s", scheme, host)
|
|
|
}
|
|
|
|
|
|
// 获取rsfHost
|
|
|
func (r *Region) GetRsfHost(useHttps bool) string {
|
|
|
return endpoint(useHttps, r.RsfHost)
|
|
|
}
|
|
|
|
|
|
// 获取io host
|
|
|
func (r *Region) GetIoHost(useHttps bool) string {
|
|
|
return endpoint(useHttps, r.IovipHost)
|
|
|
}
|
|
|
|
|
|
// 获取RsHost
|
|
|
func (r *Region) GetRsHost(useHttps bool) string {
|
|
|
return endpoint(useHttps, r.RsHost)
|
|
|
}
|
|
|
|
|
|
// 获取api host
|
|
|
func (r *Region) GetApiHost(useHttps bool) string {
|
|
|
return endpoint(useHttps, r.ApiHost)
|
|
|
}
|
|
|
|
|
|
var (
|
|
|
// regionHuadong 表示华东机房
|
|
|
regionHuadong = Region{
|
|
|
SrcUpHosts: []string{
|
|
|
"up.qiniup.com",
|
|
|
},
|
|
|
CdnUpHosts: []string{
|
|
|
"upload.qiniup.com",
|
|
|
},
|
|
|
RsHost: "rs.qbox.me",
|
|
|
RsfHost: "rsf.qbox.me",
|
|
|
ApiHost: "api.qiniu.com",
|
|
|
IovipHost: "iovip.qbox.me",
|
|
|
}
|
|
|
|
|
|
// regionHuadongZhejiang2 表示华东-浙江2
|
|
|
regionHuadongZhejiang2 = Region{
|
|
|
SrcUpHosts: []string{
|
|
|
"up-cn-east-2.qiniup.com",
|
|
|
},
|
|
|
CdnUpHosts: []string{
|
|
|
"upload-cn-east-2.qiniup.com",
|
|
|
},
|
|
|
RsHost: "rs-cn-east-2.qiniuapi.com",
|
|
|
RsfHost: "rsf-cn-east-2.qiniuapi.com",
|
|
|
ApiHost: "api-cn-east-2.qiniuapi.com",
|
|
|
IovipHost: "iovip-cn-east-2.qiniuio.com",
|
|
|
}
|
|
|
|
|
|
// regionHuabei 表示华北机房
|
|
|
regionHuabei = Region{
|
|
|
SrcUpHosts: []string{
|
|
|
"up-z1.qiniup.com",
|
|
|
},
|
|
|
CdnUpHosts: []string{
|
|
|
"upload-z1.qiniup.com",
|
|
|
},
|
|
|
RsHost: "rs-z1.qbox.me",
|
|
|
RsfHost: "rsf-z1.qbox.me",
|
|
|
ApiHost: "api-z1.qiniuapi.com",
|
|
|
IovipHost: "iovip-z1.qbox.me",
|
|
|
}
|
|
|
|
|
|
// regionHuanan 表示华南机房
|
|
|
regionHuanan = Region{
|
|
|
SrcUpHosts: []string{
|
|
|
"up-z2.qiniup.com",
|
|
|
},
|
|
|
CdnUpHosts: []string{
|
|
|
"upload-z2.qiniup.com",
|
|
|
},
|
|
|
RsHost: "rs-z2.qbox.me",
|
|
|
RsfHost: "rsf-z2.qbox.me",
|
|
|
ApiHost: "api-z2.qiniuapi.com",
|
|
|
IovipHost: "iovip-z2.qbox.me",
|
|
|
}
|
|
|
|
|
|
// regionNorthAmerica 表示北美机房
|
|
|
regionNorthAmerica = Region{
|
|
|
SrcUpHosts: []string{
|
|
|
"up-na0.qiniup.com",
|
|
|
},
|
|
|
CdnUpHosts: []string{
|
|
|
"upload-na0.qiniup.com",
|
|
|
},
|
|
|
RsHost: "rs-na0.qbox.me",
|
|
|
RsfHost: "rsf-na0.qbox.me",
|
|
|
ApiHost: "api-na0.qiniuapi.com",
|
|
|
IovipHost: "iovip-na0.qbox.me",
|
|
|
}
|
|
|
|
|
|
// regionSingapore 表示新加坡机房
|
|
|
regionSingapore = Region{
|
|
|
SrcUpHosts: []string{
|
|
|
"up-as0.qiniup.com",
|
|
|
},
|
|
|
CdnUpHosts: []string{
|
|
|
"upload-as0.qiniup.com",
|
|
|
},
|
|
|
RsHost: "rs-as0.qbox.me",
|
|
|
RsfHost: "rsf-as0.qbox.me",
|
|
|
ApiHost: "api-as0.qiniuapi.com",
|
|
|
IovipHost: "iovip-as0.qbox.me",
|
|
|
}
|
|
|
|
|
|
// regionApNortheast1 表示亚太-首尔机房
|
|
|
regionApNortheast1 = Region{
|
|
|
SrcUpHosts: []string{
|
|
|
"up-ap-northeast-1.qiniup.com",
|
|
|
},
|
|
|
CdnUpHosts: []string{
|
|
|
"upload-ap-northeast-1.qiniup.com",
|
|
|
},
|
|
|
RsHost: "rs-ap-northeast-1.qiniuapi.com",
|
|
|
RsfHost: "rsf-ap-northeast-1.qiniuapi.com",
|
|
|
ApiHost: "api-ap-northeast-1.qiniuapi.com",
|
|
|
IovipHost: "iovip-ap-northeast-1.qiniuio.com",
|
|
|
}
|
|
|
)
|
|
|
|
|
|
const (
|
|
|
// region code
|
|
|
RIDHuadong = RegionID("z0")
|
|
|
RIDHuadongZheJiang2 = RegionID("cn-east-2")
|
|
|
RIDHuabei = RegionID("z1")
|
|
|
RIDHuanan = RegionID("z2")
|
|
|
RIDNorthAmerica = RegionID("na0")
|
|
|
RIDSingapore = RegionID("as0")
|
|
|
RIDApNortheast1 = RegionID("ap-northeast-1")
|
|
|
)
|
|
|
|
|
|
// regionMap 是RegionID到具体的Region的映射
|
|
|
var regionMap = map[RegionID]Region{
|
|
|
RIDHuadong: regionHuadong,
|
|
|
RIDHuadongZheJiang2: regionHuadongZhejiang2,
|
|
|
RIDHuanan: regionHuanan,
|
|
|
RIDHuabei: regionHuabei,
|
|
|
RIDSingapore: regionSingapore,
|
|
|
RIDNorthAmerica: regionNorthAmerica,
|
|
|
RIDApNortheast1: regionApNortheast1,
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
defaultApiHost = "api.qiniu.com"
|
|
|
defaultUcHost0 = "uc.qbox.me"
|
|
|
defaultUcHost1 = "kodo-config.qiniuapi.com"
|
|
|
)
|
|
|
|
|
|
// UcHost 为查询空间相关域名的 API 服务地址
|
|
|
// 设置 UcHost 时,如果不指定 scheme 默认会使用 https
|
|
|
// Deprecated 使用 SetUcHosts 替换
|
|
|
var UcHost = ""
|
|
|
|
|
|
// 公有云包括 defaultApiHost,非 uc query api 使用时需要移除 defaultApiHost
|
|
|
// 用户配置时,不能配置 api 域名
|
|
|
var ucHosts = []string{defaultUcHost0, defaultUcHost1, defaultApiHost}
|
|
|
|
|
|
// SetUcHost
|
|
|
// Deprecated 使用 SetUcHosts 替换
|
|
|
func SetUcHost(host string, useHttps bool) {
|
|
|
if len(host) == 0 {
|
|
|
return
|
|
|
}
|
|
|
host = endpoint(useHttps, host)
|
|
|
ucHosts = []string{host}
|
|
|
}
|
|
|
|
|
|
// SetUcHosts 配置多个 UC 域名
|
|
|
func SetUcHosts(hosts ...string) {
|
|
|
var newHosts []string
|
|
|
for _, host := range hosts {
|
|
|
if len(host) > 0 {
|
|
|
newHosts = append(newHosts, host)
|
|
|
}
|
|
|
}
|
|
|
ucHosts = newHosts
|
|
|
}
|
|
|
|
|
|
func getUcHost(useHttps bool) string {
|
|
|
// 兼容老版本,优先使用 UcHost
|
|
|
host := ""
|
|
|
if len(UcHost) > 0 {
|
|
|
host = UcHost
|
|
|
} else if len(ucHosts) > 0 {
|
|
|
host = ucHosts[0]
|
|
|
}
|
|
|
return endpoint(useHttps, host)
|
|
|
}
|
|
|
|
|
|
// 不带 scheme
|
|
|
func getUcBackupHosts() []string {
|
|
|
var hosts []string
|
|
|
if len(UcHost) > 0 {
|
|
|
hosts = append(hosts, removeHostScheme(UcHost))
|
|
|
}
|
|
|
|
|
|
for _, host := range ucHosts {
|
|
|
if len(host) > 0 {
|
|
|
hosts = append(hosts, removeHostScheme(host))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
hosts = removeRepeatStringItem(hosts)
|
|
|
return hosts
|
|
|
}
|
|
|
|
|
|
// GetRegion 用来根据ak和bucket来获取空间相关的机房信息
|
|
|
// 延用 v2, v2 结构和 v4 结构不同且暂不可替代
|
|
|
// Deprecated 使用 GetRegionWithOptions 替换
|
|
|
func GetRegion(ak, bucket string) (*Region, error) {
|
|
|
return GetRegionWithOptions(ak, bucket, DefaultUCApiOptions())
|
|
|
}
|
|
|
|
|
|
// GetRegionWithOptions 用来根据ak和bucket来获取空间相关的机房信息
|
|
|
func GetRegionWithOptions(ak, bucket string, options UCApiOptions) (*Region, error) {
|
|
|
return getRegionByV2(ak, bucket, options)
|
|
|
}
|
|
|
|
|
|
// 使用 v4
|
|
|
func getRegionGroup(ak, bucket string) (*RegionGroup, error) {
|
|
|
return getRegionByV4(ak, bucket, DefaultUCApiOptions())
|
|
|
}
|
|
|
|
|
|
func getRegionGroupWithOptions(ak, bucket string, options UCApiOptions) (*RegionGroup, error) {
|
|
|
return getRegionByV4(ak, bucket, options)
|
|
|
}
|
|
|
|
|
|
type RegionInfo struct {
|
|
|
ID string `json:"id"`
|
|
|
Description string `json:"description"`
|
|
|
}
|
|
|
|
|
|
func SetRegionCachePath(newPath string) {
|
|
|
setRegionV2CachePath(newPath)
|
|
|
setRegionV4CachePath(newPath)
|
|
|
}
|
|
|
|
|
|
// GetRegionsInfo Deprecated and use GetRegionsInfoWithOptions instead
|
|
|
// Deprecated
|
|
|
func GetRegionsInfo(mac *auth.Credentials) ([]RegionInfo, error) {
|
|
|
return GetRegionsInfoWithOptions(mac, DefaultUCApiOptions())
|
|
|
}
|
|
|
|
|
|
func GetRegionsInfoWithOptions(mac *auth.Credentials, options UCApiOptions) ([]RegionInfo, error) {
|
|
|
var regions struct {
|
|
|
Regions []RegionInfo `json:"regions"`
|
|
|
}
|
|
|
|
|
|
reqUrl := getUcHost(options.UseHttps) + "/regions"
|
|
|
c := getUCClient(ucClientConfig{
|
|
|
IsUcQueryApi: false,
|
|
|
RetryMax: options.RetryMax,
|
|
|
HostFreezeDuration: options.HostFreezeDuration,
|
|
|
}, mac)
|
|
|
_, qErr := clientv2.DoAndDecodeJsonResponse(c, clientv2.RequestParams{
|
|
|
Context: context.Background(),
|
|
|
Method: clientv2.RequestMethodGet,
|
|
|
Url: reqUrl,
|
|
|
Header: nil,
|
|
|
BodyCreator: nil,
|
|
|
}, ®ions)
|
|
|
if qErr != nil {
|
|
|
return nil, fmt.Errorf("query region error, %s", qErr.Error())
|
|
|
} else {
|
|
|
return regions.Regions, nil
|
|
|
}
|
|
|
}
|
|
|
|
|
|
type ucClientConfig struct {
|
|
|
// 非 uc query api 需要去除默认域名 defaultApiHost
|
|
|
IsUcQueryApi bool
|
|
|
|
|
|
// 单域名重试次数
|
|
|
RetryMax int
|
|
|
|
|
|
// 主备域名冻结时间(默认:600s),当一个域名请求失败(单个域名会被重试 TryTimes 次),会被冻结一段时间,使用备用域名进行重试,在冻结时间内,域名不能被使用,当一个操作中所有域名竣备冻结操作不在进行重试,返回最后一次操作的错误。
|
|
|
HostFreezeDuration time.Duration
|
|
|
}
|
|
|
|
|
|
func getUCClient(config ucClientConfig, mac *auth.Credentials) clientv2.Client {
|
|
|
allHosts := getUcBackupHosts()
|
|
|
var hosts []string = nil
|
|
|
if !config.IsUcQueryApi {
|
|
|
// 非 uc query api 去除 defaultApiHost
|
|
|
for _, host := range allHosts {
|
|
|
if host != defaultApiHost {
|
|
|
hosts = append(hosts, host)
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
hosts = allHosts
|
|
|
}
|
|
|
|
|
|
is := []clientv2.Interceptor{
|
|
|
clientv2.NewHostsRetryInterceptor(clientv2.HostsRetryConfig{
|
|
|
RetryConfig: clientv2.RetryConfig{
|
|
|
RetryMax: len(hosts),
|
|
|
RetryInterval: nil,
|
|
|
ShouldRetry: nil,
|
|
|
},
|
|
|
ShouldFreezeHost: nil,
|
|
|
HostFreezeDuration: 0,
|
|
|
HostProvider: hostprovider.NewWithHosts(hosts),
|
|
|
}),
|
|
|
clientv2.NewSimpleRetryInterceptor(clientv2.RetryConfig{
|
|
|
RetryMax: config.RetryMax,
|
|
|
RetryInterval: nil,
|
|
|
ShouldRetry: nil,
|
|
|
}),
|
|
|
}
|
|
|
|
|
|
if mac != nil {
|
|
|
is = append(is, clientv2.NewAuthInterceptor(clientv2.AuthConfig{
|
|
|
Credentials: *mac,
|
|
|
TokenType: auth.TokenQiniu,
|
|
|
}))
|
|
|
}
|
|
|
|
|
|
return clientv2.NewClient(nil, is...)
|
|
|
}
|