Feat: download / preview files in slave side
This commit is contained in:
parent
35c2a5c977
commit
b19910867e
8 changed files with 107 additions and 6 deletions
|
@ -282,7 +282,7 @@ func (fs *FileSystem) signURL(ctx context.Context, file *model.File, ttl int64,
|
|||
// 签名最终URL
|
||||
// 生成外链地址
|
||||
siteURL := model.GetSiteURL()
|
||||
source, err := fs.Handler.Source(ctx, fs.FileTarget[0].SourceName, *siteURL, ttl, isDownload)
|
||||
source, err := fs.Handler.Source(ctx, fs.FileTarget[0].SourceName, *siteURL, ttl, isDownload, fs.User.Group.SpeedLimit)
|
||||
if err != nil {
|
||||
return "", serializer.NewError(serializer.CodeNotSet, "无法获取外链", err)
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ type Handler interface {
|
|||
// 获取外链/下载地址,
|
||||
// url - 站点本身地址,
|
||||
// isDownload - 是否直接下载
|
||||
Source(ctx context.Context, path string, url url.URL, ttl int64, isDownload bool) (string, error)
|
||||
Source(ctx context.Context, path string, url url.URL, ttl int64, isDownload bool, speed int) (string, error)
|
||||
|
||||
// Token 获取有效期为ttl的上传凭证和签名,同时回调会话有效期为sessionTTL
|
||||
Token(ctx context.Context, ttl int64, callbackKey string) (serializer.UploadCredential, error)
|
||||
|
|
|
@ -117,6 +117,7 @@ func (handler Handler) Source(
|
|||
baseURL url.URL,
|
||||
ttl int64,
|
||||
isDownload bool,
|
||||
speed int,
|
||||
) (string, error) {
|
||||
file, ok := ctx.Value(fsctx.FileModelCtx).(model.File)
|
||||
if !ok {
|
||||
|
|
|
@ -52,6 +52,7 @@ func (handler Handler) Source(
|
|||
baseURL url.URL,
|
||||
ttl int64,
|
||||
isDownload bool,
|
||||
speed int,
|
||||
) (string, error) {
|
||||
file, ok := ctx.Value(fsctx.FileModelCtx).(model.File)
|
||||
if !ok {
|
||||
|
@ -80,7 +81,7 @@ func (handler Handler) Source(
|
|||
authInstance := auth.HMACAuth{SecretKey: []byte(handler.Policy.SecretKey)}
|
||||
signedURI, err = auth.SignURI(
|
||||
authInstance,
|
||||
fmt.Sprintf("%s/%s", controller, sourcePath),
|
||||
fmt.Sprintf("%s/%d/%s/%s", controller, speed, sourcePath, file.Name),
|
||||
expires,
|
||||
)
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ func (m FileHeaderMock) Thumb(ctx context.Context, files string) (*response.Cont
|
|||
return args.Get(0).(*response.ContentResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m FileHeaderMock) Source(ctx context.Context, path string, url url.URL, expires int64, isDownload bool) (string, error) {
|
||||
args := m.Called(ctx, path, url, expires, isDownload)
|
||||
func (m FileHeaderMock) Source(ctx context.Context, path string, url url.URL, expires int64, isDownload bool, speed int) (string, error) {
|
||||
args := m.Called(ctx, path, url, expires, isDownload, speed)
|
||||
return args.Get(0).(string), args.Error(1)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
|
||||
"github.com/HFO4/cloudreve/pkg/filesystem/local"
|
||||
"github.com/HFO4/cloudreve/pkg/serializer"
|
||||
"github.com/HFO4/cloudreve/service/explorer"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
@ -80,3 +81,37 @@ func SlaveUpload(c *gin.Context) {
|
|||
Code: 0,
|
||||
})
|
||||
}
|
||||
|
||||
// SlaveDownload 从机文件下载
|
||||
func SlaveDownload(c *gin.Context) {
|
||||
// 创建上下文
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var service explorer.SlaveDownloadService
|
||||
if err := c.ShouldBindUri(&service); err == nil {
|
||||
res := service.ServeFile(ctx, c, true)
|
||||
if res.Code != 0 {
|
||||
c.JSON(200, res)
|
||||
}
|
||||
} else {
|
||||
c.JSON(200, ErrorResponse(err))
|
||||
}
|
||||
}
|
||||
|
||||
// SlavePreview 从机文件预览
|
||||
func SlavePreview(c *gin.Context) {
|
||||
// 创建上下文
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var service explorer.SlaveDownloadService
|
||||
if err := c.ShouldBindUri(&service); err == nil {
|
||||
res := service.ServeFile(ctx, c, false)
|
||||
if res.Code != 0 {
|
||||
c.JSON(200, res)
|
||||
}
|
||||
} else {
|
||||
c.JSON(200, ErrorResponse(err))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,12 @@ func InitSlaveRouter() *gin.Engine {
|
|||
路由
|
||||
*/
|
||||
{
|
||||
// 上传
|
||||
v3.POST("upload", controllers.SlaveUpload)
|
||||
// 下载
|
||||
v3.GET("download/:speed/:path/:name", controllers.SlaveDownload)
|
||||
// 预览 / 外链
|
||||
v3.GET("source/:speed/:path/:name", controllers.SlavePreview)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package explorer
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
model "github.com/HFO4/cloudreve/models"
|
||||
"github.com/HFO4/cloudreve/pkg/cache"
|
||||
"github.com/HFO4/cloudreve/pkg/filesystem"
|
||||
|
@ -9,6 +10,7 @@ import (
|
|||
"github.com/HFO4/cloudreve/pkg/filesystem/local"
|
||||
"github.com/HFO4/cloudreve/pkg/serializer"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/gorm"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
@ -32,6 +34,13 @@ type DownloadService struct {
|
|||
ID string `uri:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// SlaveDownloadService 从机文件下載服务
|
||||
type SlaveDownloadService struct {
|
||||
PathEncoded string `uri:"path" binding:"required"`
|
||||
Name string `uri:"name" binding:"required"`
|
||||
Speed int `uri:"speed" binding:"min=0"`
|
||||
}
|
||||
|
||||
// DownloadArchived 下載已打包的多文件
|
||||
func (service *DownloadService) DownloadArchived(ctx context.Context, c *gin.Context) serializer.Response {
|
||||
// 创建文件系统
|
||||
|
@ -166,10 +175,10 @@ func (service *DownloadService) Download(ctx context.Context, c *gin.Context) se
|
|||
// 开始处理下载
|
||||
ctx = context.WithValue(ctx, fsctx.GinCtx, c)
|
||||
rs, err := fs.GetDownloadContent(ctx, "")
|
||||
defer rs.Close()
|
||||
if err != nil {
|
||||
return serializer.Err(serializer.CodeNotSet, err.Error(), err)
|
||||
}
|
||||
defer rs.Close()
|
||||
|
||||
// 设置文件名
|
||||
c.Header("Content-Disposition", "attachment; filename=\""+url.PathEscape(fs.FileTarget[0].Name)+"\"")
|
||||
|
@ -278,3 +287,53 @@ func (service *SingleFileService) PutContent(ctx context.Context, c *gin.Context
|
|||
Code: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// ServeFile 通过签名URL的文件下载从机文件
|
||||
func (service *SlaveDownloadService) ServeFile(ctx context.Context, c *gin.Context, isDownload bool) serializer.Response {
|
||||
// 创建文件系统
|
||||
fs, err := filesystem.NewAnonymousFileSystem()
|
||||
if err != nil {
|
||||
return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err)
|
||||
}
|
||||
defer fs.Recycle()
|
||||
|
||||
// 解码文件路径
|
||||
fileSource, err := base64.RawURLEncoding.DecodeString(service.PathEncoded)
|
||||
if err != nil {
|
||||
return serializer.ParamErr("无法解析的文件地址", err)
|
||||
}
|
||||
|
||||
// 根据URL里的信息创建一个文件对象和用户对象
|
||||
file := model.File{
|
||||
Name: service.Name,
|
||||
SourceName: string(fileSource),
|
||||
Policy: model.Policy{
|
||||
Model: gorm.Model{ID: 1},
|
||||
Type: "local",
|
||||
},
|
||||
}
|
||||
fs.User = &model.User{
|
||||
Group: model.Group{SpeedLimit: service.Speed},
|
||||
}
|
||||
fs.FileTarget = []model.File{file}
|
||||
|
||||
// 开始处理下载
|
||||
ctx = context.WithValue(ctx, fsctx.GinCtx, c)
|
||||
rs, err := fs.GetDownloadContent(ctx, "")
|
||||
if err != nil {
|
||||
return serializer.Err(serializer.CodeNotSet, err.Error(), err)
|
||||
}
|
||||
defer rs.Close()
|
||||
|
||||
// 设置下载文件名
|
||||
if isDownload {
|
||||
c.Header("Content-Disposition", "attachment; filename=\""+url.PathEscape(fs.FileTarget[0].Name)+"\"")
|
||||
}
|
||||
|
||||
// 发送文件
|
||||
http.ServeContent(c.Writer, c.Request, fs.FileTarget[0].Name, time.Now(), rs)
|
||||
|
||||
return serializer.Response{
|
||||
Code: 0,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue