diff --git a/go.mod b/go.mod index 11de968..995487a 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/gin-gonic/gin v1.4.0 github.com/go-ini/ini v1.50.0 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/juju/ratelimit v1.0.1 github.com/mattn/go-colorable v0.1.4 // indirect diff --git a/pkg/filesystem/driver/cos/handller.go b/pkg/filesystem/driver/cos/handller.go index 7b81b6f..812b800 100644 --- a/pkg/filesystem/driver/cos/handller.go +++ b/pkg/filesystem/driver/cos/handller.go @@ -12,8 +12,10 @@ import ( "github.com/HFO4/cloudreve/pkg/filesystem/fsctx" "github.com/HFO4/cloudreve/pkg/filesystem/response" "github.com/HFO4/cloudreve/pkg/serializer" + "github.com/google/go-querystring/query" cossdk "github.com/tencentyun/cos-go-sdk-v5" "io" + "net/http" "net/url" "time" ) @@ -31,6 +33,11 @@ type MetaData struct { CallbackURL string } +type urlOption struct { + Speed int `url:"x-cos-traffic-limit,omitempty"` + ContentDescription string `url:"response-content-disposition,omitempty"` +} + // Driver 腾讯云COS适配器模板 type Driver struct { Policy *model.Policy @@ -87,7 +94,67 @@ func (handler Driver) Source( isDownload bool, speed int, ) (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 diff --git a/pkg/filesystem/driver/onedrive/api_test.go b/pkg/filesystem/driver/onedrive/api_test.go index a1295d2..2152f76 100644 --- a/pkg/filesystem/driver/onedrive/api_test.go +++ b/pkg/filesystem/driver/onedrive/api_test.go @@ -388,7 +388,7 @@ func TestClient_UploadChunk(t *testing.T) { ChunkSize: 10, Total: 100, Retried: 0, - Data: strings.NewReader("1231312"), + Data: []byte("12313121231312"), }) clientMock.AssertExpectations(t) asserts.NoError(err) @@ -418,7 +418,7 @@ func TestClient_UploadChunk(t *testing.T) { ChunkSize: 10, Total: 100, Retried: 0, - Data: strings.NewReader("1231312"), + Data: []byte("12313112313122"), }) clientMock.AssertExpectations(t) asserts.Error(err) @@ -448,7 +448,7 @@ func TestClient_UploadChunk(t *testing.T) { ChunkSize: 5, Total: 100, Retried: 0, - Data: strings.NewReader("1231312"), + Data: []byte("1231312"), }) clientMock.AssertExpectations(t) asserts.NoError(err) @@ -483,7 +483,7 @@ func TestClient_UploadChunk(t *testing.T) { ChunkSize: 5, Total: 100, Retried: 0, - Data: strings.NewReader("1231312"), + Data: []byte("1231312"), } res, err := client.UploadChunk(context.Background(), "http://dev.com", chunk) clientMock.AssertExpectations(t)