mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-10-31 16:49:03 +08:00 
			
		
		
		
	Feat: use goroutine to detect upload-canceling action / Object name validate
This commit is contained in:
		| @ -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 | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 HFO4
					HFO4