mirror of https://github.com/cloudreve/Cloudreve
Feat: use goroutine to detect upload-canceling action / Object name validate
parent
331931e539
commit
160f964564
|
@ -7,4 +7,5 @@ var (
|
||||||
FileSizeTooBigError = errors.New("单个文件尺寸太大")
|
FileSizeTooBigError = errors.New("单个文件尺寸太大")
|
||||||
FileExtensionNotAllowedError = errors.New("不允许上传此类型的文件")
|
FileExtensionNotAllowedError = errors.New("不允许上传此类型的文件")
|
||||||
InsufficientCapacityError = errors.New("容量空间不足")
|
InsufficientCapacityError = errors.New("容量空间不足")
|
||||||
|
IlegalObjectNameError = errors.New("目标名称非法")
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,8 +2,10 @@ package filesystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"github.com/HFO4/cloudreve/models"
|
"github.com/HFO4/cloudreve/models"
|
||||||
"github.com/HFO4/cloudreve/pkg/filesystem/local"
|
"github.com/HFO4/cloudreve/pkg/filesystem/local"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
@ -64,6 +66,10 @@ func NewFileSystem(user *model.User) (*FileSystem, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
上传处理相关
|
||||||
|
*/
|
||||||
|
|
||||||
// Upload 上传文件
|
// Upload 上传文件
|
||||||
func (fs *FileSystem) Upload(ctx context.Context, file FileData) (err error) {
|
func (fs *FileSystem) Upload(ctx context.Context, file FileData) (err error) {
|
||||||
// 上传前的钩子
|
// 上传前的钩子
|
||||||
|
@ -75,6 +81,9 @@ func (fs *FileSystem) Upload(ctx context.Context, file FileData) (err error) {
|
||||||
// 生成文件名和路径
|
// 生成文件名和路径
|
||||||
savePath := fs.GenerateSavePath(file)
|
savePath := fs.GenerateSavePath(file)
|
||||||
|
|
||||||
|
// 处理客户端未完成上传时,关闭连接
|
||||||
|
go fs.CancelUpload(ctx, savePath, file)
|
||||||
|
|
||||||
// 保存文件
|
// 保存文件
|
||||||
err = fs.Handler.Put(ctx, file, savePath)
|
err = fs.Handler.Put(ctx, file, savePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -91,3 +100,16 @@ func (fs *FileSystem) GenerateSavePath(file FileData) string {
|
||||||
fs.User.Policy.GenerateFileName(fs.User.Model.ID, file.GetFileName()),
|
fs.User.Policy.GenerateFileName(fs.User.Model.ID, file.GetFileName()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CancelUpload 监测客户端取消上传
|
||||||
|
func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileData) {
|
||||||
|
ginCtx := ctx.Value("ginCtx").(*gin.Context)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// 客户端正常关闭,不执行操作
|
||||||
|
case <-ginCtx.Request.Context().Done():
|
||||||
|
// 客户端取消了上传,删除保存的文件
|
||||||
|
fmt.Println("取消上传")
|
||||||
|
// 归还空间
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,11 @@ func GenericBeforeUpload(ctx context.Context, fs *FileSystem, file FileData) err
|
||||||
return FileSizeTooBigError
|
return FileSizeTooBigError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证文件名
|
||||||
|
if !fs.ValidateLegalName(ctx, file.GetFileName()) {
|
||||||
|
return IlegalObjectNameError
|
||||||
|
}
|
||||||
|
|
||||||
// 验证扩展名
|
// 验证扩展名
|
||||||
if !fs.ValidateExtension(ctx, file.GetFileName()) {
|
if !fs.ValidateExtension(ctx, file.GetFileName()) {
|
||||||
return FileExtensionNotAllowedError
|
return FileExtensionNotAllowedError
|
||||||
|
|
|
@ -24,12 +24,14 @@ func (handler Handler) Put(ctx context.Context, file io.ReadCloser, dst string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建目标文件
|
||||||
out, err := os.Create(dst)
|
out, err := os.Create(dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
|
|
||||||
|
// 写入文件内容
|
||||||
_, err = io.Copy(out, file)
|
_, err = io.Copy(out, file)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,19 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 文件/路径名保留字符
|
||||||
|
var reservedCharacter = []string{"\\", "?", "*", "<", "\"", ":", ">", "/"}
|
||||||
|
|
||||||
|
// ValidateLegalName 验证文件名/文件夹名是否合法
|
||||||
|
func (fs *FileSystem) ValidateLegalName(ctx context.Context, name string) bool {
|
||||||
|
for _, value := range reservedCharacter {
|
||||||
|
if strings.Contains(name, value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateFileSize 验证上传的文件大小是否超出限制
|
// ValidateFileSize 验证上传的文件大小是否超出限制
|
||||||
func (fs *FileSystem) ValidateFileSize(ctx context.Context, size uint64) bool {
|
func (fs *FileSystem) ValidateFileSize(ctx context.Context, size uint64) bool {
|
||||||
return size <= fs.User.Policy.MaxSize
|
return size <= fs.User.Policy.MaxSize
|
||||||
|
|
|
@ -34,10 +34,19 @@ func FileUpload(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileUploadStream 本地策略流式上传
|
||||||
func FileUploadStream(c *gin.Context) {
|
func FileUploadStream(c *gin.Context) {
|
||||||
|
// 创建上下文
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
// 非本地策略时拒绝上传
|
||||||
|
if user, ok := c.Get("user"); ok && user.(*model.User).Policy.Type != "local" {
|
||||||
|
c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, "当前上传策略无法使用", nil))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取得文件大小
|
||||||
fileSize, err := strconv.ParseUint(c.Request.Header.Get("Content-Length"), 10, 64)
|
fileSize, err := strconv.ParseUint(c.Request.Header.Get("Content-Length"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(200, ErrorResponse(err))
|
c.JSON(200, ErrorResponse(err))
|
||||||
|
@ -63,7 +72,8 @@ func FileUploadStream(c *gin.Context) {
|
||||||
fs.BeforeUpload = filesystem.GenericBeforeUpload
|
fs.BeforeUpload = filesystem.GenericBeforeUpload
|
||||||
|
|
||||||
// 执行上传
|
// 执行上传
|
||||||
err = fs.Upload(ctx, fileData)
|
uploadCtx := context.WithValue(ctx, "ginCtx", c)
|
||||||
|
err = fs.Upload(uploadCtx, fileData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(200, serializer.Err(serializer.CodeUploadFailed, err.Error(), err))
|
c.JSON(200, serializer.Err(serializer.CodeUploadFailed, err.Error(), err))
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in New Issue