mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-10-31 16:49:03 +08:00 
			
		
		
		
	Fix: upload was marked canceled when small file uploaded
This commit is contained in:
		| @ -15,4 +15,5 @@ var ( | ||||
| 	ErrFileExisted             = errors.New("同名文件已存在") | ||||
| 	ErrPathNotExist            = serializer.NewError(404, "路径不存在", nil) | ||||
| 	ErrObjectNotExist          = serializer.NewError(404, "文件不存在", nil) | ||||
| 	ErrIO                      = serializer.NewError(serializer.CodeIOFailed, "无法读取文件数据", nil) | ||||
| ) | ||||
|  | ||||
| @ -2,9 +2,7 @@ package filesystem | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	model "github.com/HFO4/cloudreve/models" | ||||
| 	"github.com/HFO4/cloudreve/pkg/serializer" | ||||
| 	"github.com/HFO4/cloudreve/pkg/util" | ||||
| 	"io" | ||||
| ) | ||||
| @ -37,9 +35,9 @@ func (fs *FileSystem) AddFile(ctx context.Context, parent *model.Folder) (*model | ||||
| 	return &newFile, nil | ||||
| } | ||||
|  | ||||
| // Download 处理下载文件请求,path为虚拟路径 | ||||
| // GetContent 获取文件内容,path为虚拟路径 | ||||
| // TODO:测试 | ||||
| func (fs *FileSystem) Download(ctx context.Context, path string) (io.ReadCloser, error) { | ||||
| func (fs *FileSystem) GetContent(ctx context.Context, path string) (io.ReadSeeker, error) { | ||||
| 	// 触发`下载前`钩子 | ||||
| 	err := fs.Trigger(ctx, fs.BeforeFileDownload) | ||||
| 	if err != nil { | ||||
| @ -52,6 +50,7 @@ func (fs *FileSystem) Download(ctx context.Context, path string) (io.ReadCloser, | ||||
| 	if !exist { | ||||
| 		return nil, ErrObjectNotExist | ||||
| 	} | ||||
| 	fs.Target = &file | ||||
|  | ||||
| 	// 将当前存储策略重设为文件使用的 | ||||
| 	fs.Policy = file.GetPolicy() | ||||
| @ -60,5 +59,11 @@ func (fs *FileSystem) Download(ctx context.Context, path string) (io.ReadCloser, | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return nil, serializer.NewError(serializer.CodeEncryptError, "人都的", errors.New("不是人都的")) | ||||
| 	// 获取文件流 | ||||
| 	rs, err := fs.Handler.Get(ctx, file.SourceName) | ||||
| 	if err != nil { | ||||
| 		return nil, ErrIO.WithError(err) | ||||
| 	} | ||||
|  | ||||
| 	return rs, nil | ||||
| } | ||||
|  | ||||
| @ -25,6 +25,8 @@ type Handler interface { | ||||
| 	Put(ctx context.Context, file io.ReadCloser, dst string, size uint64) error | ||||
| 	// 删除一个或多个文件 | ||||
| 	Delete(ctx context.Context, files []string) ([]string, error) | ||||
| 	// 获取文件 | ||||
| 	Get(ctx context.Context, path string) (io.ReadSeeker, error) | ||||
| } | ||||
|  | ||||
| // FileSystem 管理文件的文件系统 | ||||
| @ -33,6 +35,8 @@ type FileSystem struct { | ||||
| 	User *model.User | ||||
| 	// 操作文件使用的上传策略 | ||||
| 	Policy *model.Policy | ||||
| 	// 当前正在处理的文件对象 | ||||
| 	Target *model.File | ||||
|  | ||||
| 	/* | ||||
| 	   钩子函数 | ||||
|  | ||||
| @ -2,6 +2,7 @@ package local | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"github.com/HFO4/cloudreve/pkg/util" | ||||
| 	"io" | ||||
| 	"os" | ||||
| @ -11,6 +12,31 @@ import ( | ||||
| // Handler 本地策略适配器 | ||||
| type Handler struct{} | ||||
|  | ||||
| // Get 获取文件内容 | ||||
| // TODO:测试 | ||||
| func (handler Handler) Get(ctx context.Context, path string) (io.ReadSeeker, error) { | ||||
| 	// 打开文件 | ||||
| 	file, err := os.Open(path) | ||||
| 	if err != nil { | ||||
| 		util.Log().Debug("无法打开文件:%s", err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// 开启一个协程,用于请求结束后关闭reader | ||||
| 	go closeReader(ctx, file) | ||||
|  | ||||
| 	return file, nil | ||||
| } | ||||
|  | ||||
| // closeReader 用于在请求结束后关闭reader | ||||
| func closeReader(ctx context.Context, closer io.Closer) { | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		err := closer.Close() | ||||
| 		fmt.Println("关闭reader", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Put 将文件流保存到指定目录 | ||||
| func (handler Handler) Put(ctx context.Context, file io.ReadCloser, dst string, size uint64) error { | ||||
| 	defer file.Close() | ||||
|  | ||||
| @ -2,7 +2,6 @@ package filesystem | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"github.com/HFO4/cloudreve/pkg/util" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"path/filepath" | ||||
| @ -73,19 +72,20 @@ func (fs *FileSystem) GenerateSavePath(ctx context.Context, file FileHeader) str | ||||
| func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileHeader) { | ||||
| 	ginCtx := ctx.Value(GinCtx).(*gin.Context) | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		// 客户端正常关闭,不执行操作 | ||||
| 		fmt.Println("正常") | ||||
| 	case <-ginCtx.Request.Context().Done(): | ||||
| 		// 客户端取消了上传 | ||||
| 		fmt.Println("取消") | ||||
| 		if fs.AfterUploadCanceled == nil { | ||||
| 			return | ||||
| 		} | ||||
| 		ctx = context.WithValue(ctx, SavePathCtx, path) | ||||
| 		err := fs.Trigger(ctx, fs.AfterUploadCanceled) | ||||
| 		if err != nil { | ||||
| 			util.Log().Debug("执行 AfterUploadCanceled 钩子出错,%s", err) | ||||
| 		select { | ||||
| 		case <-ctx.Done(): | ||||
| 			// 客户端正常关闭,不执行操作 | ||||
| 		default: | ||||
| 			if fs.AfterUploadCanceled == nil { | ||||
| 				return | ||||
| 			} | ||||
| 			ctx = context.WithValue(ctx, SavePathCtx, path) | ||||
| 			err := fs.Trigger(ctx, fs.AfterUploadCanceled) | ||||
| 			if err != nil { | ||||
| 				util.Log().Debug("执行 AfterUploadCanceled 钩子出错,%s", err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -26,6 +26,12 @@ func NewError(code int, msg string, err error) AppError { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithError 将应用error携带标准库中的error | ||||
| func (err *AppError) WithError(raw error) AppError { | ||||
| 	err.RawError = raw | ||||
| 	return *err | ||||
| } | ||||
|  | ||||
| // Error 返回业务代码确定的可读错误信息 | ||||
| func (err AppError) Error() string { | ||||
| 	return err.Msg | ||||
|  | ||||
| @ -22,7 +22,9 @@ func Download(c *gin.Context) { | ||||
| 	var service explorer.FileDownloadService | ||||
| 	if err := c.ShouldBindQuery(&service); err == nil { | ||||
| 		res := service.Download(ctx, c) | ||||
| 		c.JSON(200, res) | ||||
| 		if res.Code != 0 { | ||||
| 			c.JSON(200, res) | ||||
| 		} | ||||
| 	} else { | ||||
| 		c.JSON(200, ErrorResponse(err)) | ||||
| 	} | ||||
|  | ||||
| @ -5,6 +5,8 @@ import ( | ||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem" | ||||
| 	"github.com/HFO4/cloudreve/pkg/serializer" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // FileDownloadService 文件下载服务,path为文件完整路径 | ||||
| @ -22,12 +24,16 @@ func (service *FileDownloadService) Download(ctx context.Context, c *gin.Context | ||||
|  | ||||
| 	// 开始处理下载 | ||||
| 	ctx = context.WithValue(ctx, filesystem.GinCtx, c) | ||||
| 	_, err = fs.Download(ctx, service.Path) | ||||
|  | ||||
| 	rs, err := fs.GetContent(ctx, service.Path) | ||||
| 	if err != nil { | ||||
| 		return serializer.Err(serializer.CodeNotSet, err.Error(), err) | ||||
| 	} | ||||
|  | ||||
| 	// 设置文件名 | ||||
| 	c.Header("Content-Disposition", "attachment; filename=\""+fs.Target.Name+"\"") | ||||
| 	// 发送文件 | ||||
| 	http.ServeContent(c.Writer, c.Request, "", time.Time{}, rs) | ||||
|  | ||||
| 	return serializer.Response{ | ||||
| 		Code: 0, | ||||
| 	} | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 HFO4
					HFO4