Feat: API for receiviing chunk data

pull/1107/head
HFO4 2022-02-27 14:11:01 +08:00
parent 72173bf894
commit c301bd6045
8 changed files with 89 additions and 86 deletions

View File

@ -218,5 +218,6 @@ func (handler Driver) Source(
func (handler Driver) Token(ctx context.Context, ttl int64, uploadSession *serializer.UploadSession, file fsctx.FileHeader) (serializer.UploadCredential, error) { func (handler Driver) Token(ctx context.Context, ttl int64, uploadSession *serializer.UploadSession, file fsctx.FileHeader) (serializer.UploadCredential, error) {
return serializer.UploadCredential{ return serializer.UploadCredential{
SessionID: uploadSession.Key, SessionID: uploadSession.Key,
ChunkSize: handler.Policy.OptionsSerialized.ChunkSize,
}, nil }, nil
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"os" "os"
"path" "path"
"time"
model "github.com/cloudreve/Cloudreve/v3/models" model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/cache" "github.com/cloudreve/Cloudreve/v3/pkg/cache"
@ -167,6 +168,7 @@ func (fs *FileSystem) CreateUploadSession(ctx context.Context, file *fsctx.FileS
fs.Use("BeforeUpload", HookValidateFile) fs.Use("BeforeUpload", HookValidateFile)
fs.Use("AfterUpload", HookClearFileHeaderSize) fs.Use("AfterUpload", HookClearFileHeaderSize)
// TODO: 只有本机策略才添加文件
fs.Use("AfterUpload", GenericAfterUpload) fs.Use("AfterUpload", GenericAfterUpload)
if err := fs.Upload(ctx, file); err != nil { if err := fs.Upload(ctx, file); err != nil {
return nil, err return nil, err
@ -200,6 +202,9 @@ func (fs *FileSystem) CreateUploadSession(ctx context.Context, file *fsctx.FileS
return nil, err return nil, err
} }
// 补全上传凭证其他信息
credential.Expires = time.Now().Add(time.Duration(callBackSessionTTL) * time.Second).Unix()
return &credential, nil return &credential, nil
} }

View File

@ -46,12 +46,11 @@ type Object struct {
// PolicySummary 用于前端组件使用的存储策略概况 // PolicySummary 用于前端组件使用的存储策略概况
type PolicySummary struct { type PolicySummary struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
MaxSize uint64 `json:"max_size"` MaxSize uint64 `json:"max_size"`
FileType []string `json:"file_type"` FileType []string `json:"file_type"`
ChunkSize uint64 `json:"chunk_size"`
} }
// BuildObjectList 构建列目录响应 // BuildObjectList 构建列目录响应
@ -66,12 +65,11 @@ func BuildObjectList(parent uint, objects []Object, policy *model.Policy) Object
if policy != nil { if policy != nil {
res.Policy = &PolicySummary{ res.Policy = &PolicySummary{
ID: hashid.HashID(policy.ID, hashid.PolicyID), ID: hashid.HashID(policy.ID, hashid.PolicyID),
Name: policy.Name, Name: policy.Name,
Type: policy.Type, Type: policy.Type,
MaxSize: policy.MaxSize, MaxSize: policy.MaxSize,
FileType: policy.OptionsSerialized.FileType, FileType: policy.OptionsSerialized.FileType,
ChunkSize: policy.OptionsSerialized.ChunkSize,
} }
} }

View File

@ -20,6 +20,8 @@ type UploadPolicy struct {
// UploadCredential 返回给客户端的上传凭证 // UploadCredential 返回给客户端的上传凭证
type UploadCredential struct { type UploadCredential struct {
SessionID string `json:"sessionID"` SessionID string `json:"sessionID"`
ChunkSize uint64 `json:"chunkSize"` // 分块大小0 为部分快
Expires int64 `json:"expires"` // 上传凭证过期时间, Unix 时间戳
Token string `json:"token"` Token string `json:"token"`
Policy string `json:"policy"` Policy string `json:"policy"`
@ -39,7 +41,7 @@ type UploadSession struct {
Name string // 文件名 Name string // 文件名
Size uint64 // 文件大小 Size uint64 // 文件大小
SavePath string // 物理存储路径,包含物理文件名 SavePath string // 物理存储路径,包含物理文件名
ChunkSize uint64 // 分块大小0 为分快 ChunkSize uint64 // 分块大小0 为分快
LastModified *time.Time // 可选的文件最后修改日期 LastModified *time.Time // 可选的文件最后修改日期
} }

View File

@ -31,14 +31,6 @@ type User struct {
Tags []tag `json:"tags"` Tags []tag `json:"tags"`
} }
type policy struct {
SaveType string `json:"saveType"`
MaxSize string `json:"maxSize"`
AllowedType []string `json:"allowedType"`
UploadURL string `json:"upUrl"`
AllowGetSource bool `json:"allowSource"`
}
type group struct { type group struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`

View File

@ -3,15 +3,10 @@ package controllers
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/cloudreve/Cloudreve/v3/pkg/request"
"net/http" "net/http"
"net/url"
"strconv"
"sync"
"github.com/cloudreve/Cloudreve/v3/pkg/conf" "github.com/cloudreve/Cloudreve/v3/pkg/conf"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/service/explorer" "github.com/cloudreve/Cloudreve/v3/service/explorer"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -285,72 +280,65 @@ func PutContent(c *gin.Context) {
} }
} }
// FileUploadStream 本地策略流式上传 // FileUpload 本地策略文件上传
func FileUploadStream(c *gin.Context) { func FileUpload(c *gin.Context) {
// 创建上下文 // 创建上下文
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
// 取得文件大小 var service explorer.UploadService
fileSize, err := strconv.ParseUint(c.Request.Header.Get("Content-Length"), 10, 64) if err := c.ShouldBindUri(&service); err == nil {
if err != nil { res := service.Upload(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err)) c.JSON(200, ErrorResponse(err))
return
} }
// 解码文件名和路径 //fileData := fsctx.FileStream{
fileName, err := url.QueryUnescape(c.Request.Header.Get("X-Cr-FileName")) // MIMEType: c.Request.Header.Get("Content-Type"),
filePath, err := url.QueryUnescape(c.Request.Header.Get("X-Cr-Path")) // File: c.Request.Body,
if err != nil { // Size: fileSize,
c.JSON(200, ErrorResponse(err)) // Name: fileName,
return // VirtualPath: filePath,
} // Mode: fsctx.Create,
//}
fileData := fsctx.FileStream{ //
MIMEType: c.Request.Header.Get("Content-Type"), //// 创建文件系统
File: c.Request.Body, //fs, err := filesystem.NewFileSystemFromContext(c)
Size: fileSize, //if err != nil {
Name: fileName, // c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err))
VirtualPath: filePath, // return
Mode: fsctx.Create, //}
} //
//// 非可用策略时拒绝上传
// 创建文件系统 //if !fs.Policy.IsTransitUpload(fileSize) {
fs, err := filesystem.NewFileSystemFromContext(c) // request.BlackHole(c.Request.Body)
if err != nil { // c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, "当前存储策略无法使用", nil))
c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err)) // return
return //}
} //
//// 给文件系统分配钩子
// 非可用策略时拒绝上传 //fs.Use("BeforeUpload", filesystem.HookValidateFile)
if !fs.Policy.IsTransitUpload(fileSize) { //fs.Use("BeforeUpload", filesystem.HookValidateCapacity)
request.BlackHole(c.Request.Body) //fs.Use("AfterUploadCanceled", filesystem.HookDeleteTempFile)
c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, "当前存储策略无法使用", nil)) //fs.Use("AfterUploadCanceled", filesystem.HookGiveBackCapacity)
return //fs.Use("AfterUpload", filesystem.GenericAfterUpload)
} //fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile)
//fs.Use("AfterValidateFailed", filesystem.HookGiveBackCapacity)
// 给文件系统分配钩子 //fs.Use("AfterUploadFailed", filesystem.HookGiveBackCapacity)
fs.Use("BeforeUpload", filesystem.HookValidateFile) //
fs.Use("BeforeUpload", filesystem.HookValidateCapacity) //// 执行上传
fs.Use("AfterUploadCanceled", filesystem.HookDeleteTempFile) //ctx = context.WithValue(ctx, fsctx.ValidateCapacityOnceCtx, &sync.Once{})
fs.Use("AfterUploadCanceled", filesystem.HookGiveBackCapacity) //uploadCtx := context.WithValue(ctx, fsctx.GinCtx, c)
fs.Use("AfterUpload", filesystem.GenericAfterUpload) //err = fs.Upload(uploadCtx, &fileData)
fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile) //if err != nil {
fs.Use("AfterValidateFailed", filesystem.HookGiveBackCapacity) // c.JSON(200, serializer.Err(serializer.CodeUploadFailed, err.Error(), err))
fs.Use("AfterUploadFailed", filesystem.HookGiveBackCapacity) // return
//}
// 执行上传 //
ctx = context.WithValue(ctx, fsctx.ValidateCapacityOnceCtx, &sync.Once{}) //c.JSON(200, serializer.Response{
uploadCtx := context.WithValue(ctx, fsctx.GinCtx, c) // Code: 0,
err = fs.Upload(uploadCtx, &fileData) //})
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeUploadFailed, err.Error(), err))
return
}
c.JSON(200, serializer.Response{
Code: 0,
})
} }
// GetUploadCredential 创建上传会话 // GetUploadCredential 创建上传会话

View File

@ -504,8 +504,10 @@ func InitMasterRouter() *gin.Engine {
// 文件 // 文件
file := auth.Group("file", middleware.HashID(hashid.FileID)) file := auth.Group("file", middleware.HashID(hashid.FileID))
{ {
// 文件上传
file.POST("upload/:sessionId/:index", controllers.FileUpload)
// 创建上传会话 // 创建上传会话
file.PUT("upload/session", controllers.GetUploadCredential) file.PUT("upload", controllers.GetUploadCredential)
// 更新文件 // 更新文件
file.PUT("update/:id", controllers.PutContent) file.PUT("update/:id", controllers.PutContent)
// 创建空白文件 // 创建空白文件

View File

@ -2,6 +2,7 @@ package explorer
import ( import (
"context" "context"
"github.com/cloudreve/Cloudreve/v3/pkg/request"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
@ -57,3 +58,17 @@ func (service *UploadSessionService) Create(ctx context.Context, c *gin.Context)
Data: credential, Data: credential,
} }
} }
// UploadService 本机策略上传服务
type UploadService struct {
ID string `uri:"sessionId" binding:"required"`
Index int `uri:"index"`
}
// Upload 处理本机文件分片上传
func (service *UploadService) Upload(ctx context.Context, c *gin.Context) serializer.Response {
request.BlackHole(c.Request.Body)
return serializer.Response{
Code: 0,
}
}