mirror of
https://github.com/halejohn/Cloudreve.git
synced 2026-01-26 09:34:57 +08:00
Feat: download file and get file downloading session
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/HFO4/cloudreve/pkg/util"
|
||||
"github.com/juju/ratelimit"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
/* ============
|
||||
@@ -105,8 +106,8 @@ func (fs *FileSystem) GetContent(ctx context.Context, path string) (io.ReadSeeke
|
||||
return nil, ErrObjectNotExist
|
||||
}
|
||||
fs.FileTarget = []model.File{*file}
|
||||
ctx = context.WithValue(ctx, fsctx.FileModelCtx, file)
|
||||
}
|
||||
ctx = context.WithValue(ctx, fsctx.FileModelCtx, fs.FileTarget[0])
|
||||
|
||||
// 将当前存储策略重设为文件使用的
|
||||
fs.Policy = fs.FileTarget[0].GetPolicy()
|
||||
@@ -173,6 +174,45 @@ func (fs *FileSystem) GroupFileByPolicy(ctx context.Context, files []model.File)
|
||||
return policyGroup
|
||||
}
|
||||
|
||||
// GetDownloadURL 创建文件下载链接
|
||||
func (fs *FileSystem) GetDownloadURL(ctx context.Context, path string) (string, error) {
|
||||
// 找到文件
|
||||
if len(fs.FileTarget) == 0 {
|
||||
exist, file := fs.IsFileExist(path)
|
||||
if !exist {
|
||||
return "", ErrObjectNotExist
|
||||
}
|
||||
fs.FileTarget = []model.File{*file}
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, fsctx.FileModelCtx, fs.FileTarget[0])
|
||||
|
||||
// 将当前存储策略重设为文件使用的
|
||||
fs.Policy = fs.FileTarget[0].GetPolicy()
|
||||
err := fs.dispatchHandler()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 生成下載地址
|
||||
siteURL := model.GetSiteURL()
|
||||
ttl, err := strconv.ParseInt(model.GetSettingByName("download_timeout"), 10, 64)
|
||||
if err != nil {
|
||||
return "", serializer.NewError(serializer.CodeInternalSetting, "无法获取下载地址有效期", err)
|
||||
}
|
||||
source, err := fs.Handler.GetDownloadURL(
|
||||
ctx,
|
||||
fs.FileTarget[0].SourceName,
|
||||
*siteURL,
|
||||
ttl,
|
||||
)
|
||||
if err != nil {
|
||||
return "", serializer.NewError(serializer.CodeNotSet, "无法获取下载地址", err)
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// GetSource 获取可直接访问文件的外链地址
|
||||
func (fs *FileSystem) GetSource(ctx context.Context, fileID uint) (string, error) {
|
||||
// 查找文件记录
|
||||
|
||||
@@ -32,6 +32,8 @@ type Handler interface {
|
||||
Thumb(ctx context.Context, path string) (*response.ContentResponse, error)
|
||||
// 获取外链地址,url
|
||||
Source(ctx context.Context, path string, url url.URL, expires int64) (string, error)
|
||||
//获取下载地址
|
||||
GetDownloadURL(ctx context.Context, path string, url url.URL, expires int64) (string, error)
|
||||
}
|
||||
|
||||
// FileSystem 管理文件的文件系统
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
model "github.com/HFO4/cloudreve/models"
|
||||
"github.com/HFO4/cloudreve/pkg/auth"
|
||||
"github.com/HFO4/cloudreve/pkg/cache"
|
||||
"github.com/HFO4/cloudreve/pkg/conf"
|
||||
"github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
|
||||
"github.com/HFO4/cloudreve/pkg/filesystem/response"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Handler 本地策略适配器
|
||||
@@ -127,3 +129,29 @@ func (handler Handler) Source(ctx context.Context, path string, url url.URL, exp
|
||||
finalURL := url.ResolveReference(signedURI).String()
|
||||
return finalURL, nil
|
||||
}
|
||||
|
||||
func (handler Handler) GetDownloadURL(ctx context.Context, path string, url url.URL, ttl int64) (string, error) {
|
||||
file, ok := ctx.Value(fsctx.FileModelCtx).(model.File)
|
||||
if !ok {
|
||||
return "", errors.New("无法获取文件记录上下文")
|
||||
}
|
||||
|
||||
// 创建下载会话,将文件信息写入缓存
|
||||
downloadSessionID := util.RandStringRunes(16)
|
||||
err := cache.Set("download_"+downloadSessionID, file, int(ttl))
|
||||
if err != nil {
|
||||
return "", serializer.NewError(serializer.CodeCacheOperation, "无法创建下載会话", err)
|
||||
}
|
||||
|
||||
// 签名生成文件记录
|
||||
signedURI, err := auth.SignURI(
|
||||
fmt.Sprintf("/api/v3/file/download/%s", downloadSessionID),
|
||||
time.Now().Unix()+ttl,
|
||||
)
|
||||
if err != nil {
|
||||
return "", serializer.NewError(serializer.CodeEncryptError, "无法对URL进行签名", err)
|
||||
}
|
||||
|
||||
finalURL := url.ResolveReference(signedURI).String()
|
||||
return finalURL, nil
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@ func (m FileHeaderMock) Source(ctx context.Context, path string, url url.URL, ex
|
||||
args := m.Called(ctx, path, url, expires)
|
||||
return args.Get(0).(string), args.Error(1)
|
||||
}
|
||||
func (m FileHeaderMock) GetDownloadURL(ctx context.Context, path string, url url.URL, expires int64) (string, error) {
|
||||
args := m.Called(ctx, path, url, expires)
|
||||
return args.Get(0).(string), args.Error(1)
|
||||
}
|
||||
|
||||
func TestFileSystem_Upload(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
@@ -66,6 +66,10 @@ const (
|
||||
CodeEncryptError = 50002
|
||||
// CodeIOFailed IO操作失败
|
||||
CodeIOFailed = 50004
|
||||
// CodeInternalSetting 内部设置参数错误
|
||||
CodeInternalSetting = 50005
|
||||
// CodeCacheOperation 缓存操作失败
|
||||
CodeCacheOperation = 50006
|
||||
//CodeParamErr 各种奇奇怪怪的参数错误
|
||||
CodeParamErr = 40001
|
||||
// CodeNotSet 未定错误,后续尝试从error中获取
|
||||
|
||||
Reference in New Issue
Block a user