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("同名文件已存在") | 	ErrFileExisted             = errors.New("同名文件已存在") | ||||||
| 	ErrPathNotExist            = serializer.NewError(404, "路径不存在", nil) | 	ErrPathNotExist            = serializer.NewError(404, "路径不存在", nil) | ||||||
| 	ErrObjectNotExist          = serializer.NewError(404, "文件不存在", nil) | 	ErrObjectNotExist          = serializer.NewError(404, "文件不存在", nil) | ||||||
|  | 	ErrIO                      = serializer.NewError(serializer.CodeIOFailed, "无法读取文件数据", nil) | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -2,9 +2,7 @@ package filesystem | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" |  | ||||||
| 	model "github.com/HFO4/cloudreve/models" | 	model "github.com/HFO4/cloudreve/models" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/serializer" |  | ||||||
| 	"github.com/HFO4/cloudreve/pkg/util" | 	"github.com/HFO4/cloudreve/pkg/util" | ||||||
| 	"io" | 	"io" | ||||||
| ) | ) | ||||||
| @ -37,9 +35,9 @@ func (fs *FileSystem) AddFile(ctx context.Context, parent *model.Folder) (*model | |||||||
| 	return &newFile, nil | 	return &newFile, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Download 处理下载文件请求,path为虚拟路径 | // GetContent 获取文件内容,path为虚拟路径 | ||||||
| // TODO:测试 | // 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) | 	err := fs.Trigger(ctx, fs.BeforeFileDownload) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -52,6 +50,7 @@ func (fs *FileSystem) Download(ctx context.Context, path string) (io.ReadCloser, | |||||||
| 	if !exist { | 	if !exist { | ||||||
| 		return nil, ErrObjectNotExist | 		return nil, ErrObjectNotExist | ||||||
| 	} | 	} | ||||||
|  | 	fs.Target = &file | ||||||
|  |  | ||||||
| 	// 将当前存储策略重设为文件使用的 | 	// 将当前存储策略重设为文件使用的 | ||||||
| 	fs.Policy = file.GetPolicy() | 	fs.Policy = file.GetPolicy() | ||||||
| @ -60,5 +59,11 @@ func (fs *FileSystem) Download(ctx context.Context, path string) (io.ReadCloser, | |||||||
| 		return nil, err | 		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 | 	Put(ctx context.Context, file io.ReadCloser, dst string, size uint64) error | ||||||
| 	// 删除一个或多个文件 | 	// 删除一个或多个文件 | ||||||
| 	Delete(ctx context.Context, files []string) ([]string, error) | 	Delete(ctx context.Context, files []string) ([]string, error) | ||||||
|  | 	// 获取文件 | ||||||
|  | 	Get(ctx context.Context, path string) (io.ReadSeeker, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| // FileSystem 管理文件的文件系统 | // FileSystem 管理文件的文件系统 | ||||||
| @ -33,6 +35,8 @@ type FileSystem struct { | |||||||
| 	User *model.User | 	User *model.User | ||||||
| 	// 操作文件使用的上传策略 | 	// 操作文件使用的上传策略 | ||||||
| 	Policy *model.Policy | 	Policy *model.Policy | ||||||
|  | 	// 当前正在处理的文件对象 | ||||||
|  | 	Target *model.File | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 	   钩子函数 | 	   钩子函数 | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ package local | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"fmt" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/util" | 	"github.com/HFO4/cloudreve/pkg/util" | ||||||
| 	"io" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
| @ -11,6 +12,31 @@ import ( | |||||||
| // Handler 本地策略适配器 | // Handler 本地策略适配器 | ||||||
| type Handler struct{} | 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 将文件流保存到指定目录 | // Put 将文件流保存到指定目录 | ||||||
| func (handler Handler) Put(ctx context.Context, file io.ReadCloser, dst string, size uint64) error { | func (handler Handler) Put(ctx context.Context, file io.ReadCloser, dst string, size uint64) error { | ||||||
| 	defer file.Close() | 	defer file.Close() | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ package filesystem | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/HFO4/cloudreve/pkg/util" | 	"github.com/HFO4/cloudreve/pkg/util" | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
| 	"path/filepath" | 	"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) { | func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileHeader) { | ||||||
| 	ginCtx := ctx.Value(GinCtx).(*gin.Context) | 	ginCtx := ctx.Value(GinCtx).(*gin.Context) | ||||||
| 	select { | 	select { | ||||||
| 	case <-ctx.Done(): |  | ||||||
| 		// 客户端正常关闭,不执行操作 |  | ||||||
| 		fmt.Println("正常") |  | ||||||
| 	case <-ginCtx.Request.Context().Done(): | 	case <-ginCtx.Request.Context().Done(): | ||||||
| 		// 客户端取消了上传 | 		select { | ||||||
| 		fmt.Println("取消") | 		case <-ctx.Done(): | ||||||
| 		if fs.AfterUploadCanceled == nil { | 			// 客户端正常关闭,不执行操作 | ||||||
| 			return | 		default: | ||||||
| 		} | 			if fs.AfterUploadCanceled == nil { | ||||||
| 		ctx = context.WithValue(ctx, SavePathCtx, path) | 				return | ||||||
| 		err := fs.Trigger(ctx, fs.AfterUploadCanceled) | 			} | ||||||
| 		if err != nil { | 			ctx = context.WithValue(ctx, SavePathCtx, path) | ||||||
| 			util.Log().Debug("执行 AfterUploadCanceled 钩子出错,%s", err) | 			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 返回业务代码确定的可读错误信息 | // Error 返回业务代码确定的可读错误信息 | ||||||
| func (err AppError) Error() string { | func (err AppError) Error() string { | ||||||
| 	return err.Msg | 	return err.Msg | ||||||
|  | |||||||
| @ -22,7 +22,9 @@ func Download(c *gin.Context) { | |||||||
| 	var service explorer.FileDownloadService | 	var service explorer.FileDownloadService | ||||||
| 	if err := c.ShouldBindQuery(&service); err == nil { | 	if err := c.ShouldBindQuery(&service); err == nil { | ||||||
| 		res := service.Download(ctx, c) | 		res := service.Download(ctx, c) | ||||||
| 		c.JSON(200, res) | 		if res.Code != 0 { | ||||||
|  | 			c.JSON(200, res) | ||||||
|  | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		c.JSON(200, ErrorResponse(err)) | 		c.JSON(200, ErrorResponse(err)) | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ import ( | |||||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem" | 	"github.com/HFO4/cloudreve/pkg/filesystem" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/serializer" | 	"github.com/HFO4/cloudreve/pkg/serializer" | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"net/http" | ||||||
|  | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // FileDownloadService 文件下载服务,path为文件完整路径 | // FileDownloadService 文件下载服务,path为文件完整路径 | ||||||
| @ -22,12 +24,16 @@ func (service *FileDownloadService) Download(ctx context.Context, c *gin.Context | |||||||
|  |  | ||||||
| 	// 开始处理下载 | 	// 开始处理下载 | ||||||
| 	ctx = context.WithValue(ctx, filesystem.GinCtx, c) | 	ctx = context.WithValue(ctx, filesystem.GinCtx, c) | ||||||
| 	_, err = fs.Download(ctx, service.Path) | 	rs, err := fs.GetContent(ctx, service.Path) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return serializer.Err(serializer.CodeNotSet, err.Error(), err) | 		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{ | 	return serializer.Response{ | ||||||
| 		Code: 0, | 		Code: 0, | ||||||
| 	} | 	} | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 HFO4
					HFO4