mirror of https://github.com/cloudreve/Cloudreve
Feat: get source url in COS
parent
dc32e85492
commit
c34f211d7e
1
go.mod
1
go.mod
|
@ -15,6 +15,7 @@ require (
|
||||||
github.com/gin-gonic/gin v1.4.0
|
github.com/gin-gonic/gin v1.4.0
|
||||||
github.com/go-ini/ini v1.50.0
|
github.com/go-ini/ini v1.50.0
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible
|
github.com/gomodule/redigo v2.0.0+incompatible
|
||||||
|
github.com/google/go-querystring v1.0.0
|
||||||
github.com/jinzhu/gorm v1.9.11
|
github.com/jinzhu/gorm v1.9.11
|
||||||
github.com/juju/ratelimit v1.0.1
|
github.com/juju/ratelimit v1.0.1
|
||||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||||
|
|
|
@ -12,8 +12,10 @@ import (
|
||||||
"github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
|
"github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
|
||||||
"github.com/HFO4/cloudreve/pkg/filesystem/response"
|
"github.com/HFO4/cloudreve/pkg/filesystem/response"
|
||||||
"github.com/HFO4/cloudreve/pkg/serializer"
|
"github.com/HFO4/cloudreve/pkg/serializer"
|
||||||
|
"github.com/google/go-querystring/query"
|
||||||
cossdk "github.com/tencentyun/cos-go-sdk-v5"
|
cossdk "github.com/tencentyun/cos-go-sdk-v5"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -31,6 +33,11 @@ type MetaData struct {
|
||||||
CallbackURL string
|
CallbackURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type urlOption struct {
|
||||||
|
Speed int `url:"x-cos-traffic-limit,omitempty"`
|
||||||
|
ContentDescription string `url:"response-content-disposition,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Driver 腾讯云COS适配器模板
|
// Driver 腾讯云COS适配器模板
|
||||||
type Driver struct {
|
type Driver struct {
|
||||||
Policy *model.Policy
|
Policy *model.Policy
|
||||||
|
@ -87,7 +94,67 @@ func (handler Driver) Source(
|
||||||
isDownload bool,
|
isDownload bool,
|
||||||
speed int,
|
speed int,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
return "", errors.New("未实现")
|
// 尝试从上下文获取文件名
|
||||||
|
fileName := ""
|
||||||
|
if file, ok := ctx.Value(fsctx.FileModelCtx).(model.File); ok {
|
||||||
|
fileName = file.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加各项设置
|
||||||
|
options := urlOption{}
|
||||||
|
if speed > 0 {
|
||||||
|
if speed < 819200 {
|
||||||
|
speed = 819200
|
||||||
|
}
|
||||||
|
if speed > 838860800 {
|
||||||
|
speed = 838860800
|
||||||
|
}
|
||||||
|
options.Speed = speed
|
||||||
|
}
|
||||||
|
if isDownload {
|
||||||
|
options.ContentDescription = "attachment; filename=\"" + url.PathEscape(fileName) + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler.signSourceURL(ctx, path, ttl, &options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler Driver) signSourceURL(ctx context.Context, path string, ttl int64, options *urlOption) (string, error) {
|
||||||
|
cdnURL, err := url.Parse(handler.Policy.BaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 公有空间不需要签名
|
||||||
|
if !handler.Policy.IsPrivate {
|
||||||
|
file, err := url.Parse(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非签名URL不支持设置响应header
|
||||||
|
options.ContentDescription = ""
|
||||||
|
|
||||||
|
optionQuery, err := query.Values(*options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
file.RawQuery = optionQuery.Encode()
|
||||||
|
sourceURL := cdnURL.ResolveReference(file)
|
||||||
|
|
||||||
|
return sourceURL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
presignedURL, err := handler.Client.Object.GetPresignedURL(ctx, http.MethodGet, path,
|
||||||
|
handler.Policy.AccessKey, handler.Policy.SecretKey, time.Duration(ttl)*time.Second, options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将最终生成的签名URL域名换成用户自定义的加速域名(如果有)
|
||||||
|
presignedURL.Host = cdnURL.Host
|
||||||
|
presignedURL.Scheme = cdnURL.Scheme
|
||||||
|
|
||||||
|
return presignedURL.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Token 获取上传策略和认证Token
|
// Token 获取上传策略和认证Token
|
||||||
|
|
|
@ -388,7 +388,7 @@ func TestClient_UploadChunk(t *testing.T) {
|
||||||
ChunkSize: 10,
|
ChunkSize: 10,
|
||||||
Total: 100,
|
Total: 100,
|
||||||
Retried: 0,
|
Retried: 0,
|
||||||
Data: strings.NewReader("1231312"),
|
Data: []byte("12313121231312"),
|
||||||
})
|
})
|
||||||
clientMock.AssertExpectations(t)
|
clientMock.AssertExpectations(t)
|
||||||
asserts.NoError(err)
|
asserts.NoError(err)
|
||||||
|
@ -418,7 +418,7 @@ func TestClient_UploadChunk(t *testing.T) {
|
||||||
ChunkSize: 10,
|
ChunkSize: 10,
|
||||||
Total: 100,
|
Total: 100,
|
||||||
Retried: 0,
|
Retried: 0,
|
||||||
Data: strings.NewReader("1231312"),
|
Data: []byte("12313112313122"),
|
||||||
})
|
})
|
||||||
clientMock.AssertExpectations(t)
|
clientMock.AssertExpectations(t)
|
||||||
asserts.Error(err)
|
asserts.Error(err)
|
||||||
|
@ -448,7 +448,7 @@ func TestClient_UploadChunk(t *testing.T) {
|
||||||
ChunkSize: 5,
|
ChunkSize: 5,
|
||||||
Total: 100,
|
Total: 100,
|
||||||
Retried: 0,
|
Retried: 0,
|
||||||
Data: strings.NewReader("1231312"),
|
Data: []byte("1231312"),
|
||||||
})
|
})
|
||||||
clientMock.AssertExpectations(t)
|
clientMock.AssertExpectations(t)
|
||||||
asserts.NoError(err)
|
asserts.NoError(err)
|
||||||
|
@ -483,7 +483,7 @@ func TestClient_UploadChunk(t *testing.T) {
|
||||||
ChunkSize: 5,
|
ChunkSize: 5,
|
||||||
Total: 100,
|
Total: 100,
|
||||||
Retried: 0,
|
Retried: 0,
|
||||||
Data: strings.NewReader("1231312"),
|
Data: []byte("1231312"),
|
||||||
}
|
}
|
||||||
res, err := client.UploadChunk(context.Background(), "http://dev.com", chunk)
|
res, err := client.UploadChunk(context.Background(), "http://dev.com", chunk)
|
||||||
clientMock.AssertExpectations(t)
|
clientMock.AssertExpectations(t)
|
||||||
|
|
Loading…
Reference in New Issue