mirror of https://github.com/cloudreve/Cloudreve
Fix: upload was marked canceled when small file uploaded
parent
4156a71adf
commit
29def02489
|
@ -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"
|
||||||
|
@ -72,13 +71,12 @@ func (fs *FileSystem) GenerateSavePath(ctx context.Context, file FileHeader) str
|
||||||
// CancelUpload 监测客户端取消上传
|
// CancelUpload 监测客户端取消上传
|
||||||
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 {
|
||||||
|
case <-ginCtx.Request.Context().Done():
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// 客户端正常关闭,不执行操作
|
// 客户端正常关闭,不执行操作
|
||||||
fmt.Println("正常")
|
default:
|
||||||
case <-ginCtx.Request.Context().Done():
|
|
||||||
// 客户端取消了上传
|
|
||||||
fmt.Println("取消")
|
|
||||||
if fs.AfterUploadCanceled == nil {
|
if fs.AfterUploadCanceled == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -88,4 +86,6 @@ func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileHe
|
||||||
util.Log().Debug("执行 AfterUploadCanceled 钩子出错,%s", err)
|
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)
|
||||||
|
if res.Code != 0 {
|
||||||
c.JSON(200, res)
|
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,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue