Feat: disable overwrite for non-updating put request, only works under local,slave,OneDrive,OSS policy. (#764)
This commit is contained in:
parent
c949d47161
commit
5e226efea1
16 changed files with 67 additions and 15 deletions
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit 3ffab03068e88d627825710798b5cc0d5ce8df20
|
||||
Subproject commit e2d4f13a54dfd424cfbc129664772e104ccf97fc
|
|
@ -100,6 +100,14 @@ func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, s
|
|||
defer file.Close()
|
||||
dst = util.RelativePath(filepath.FromSlash(dst))
|
||||
|
||||
// 如果禁止了 Overwrite,则检查是否有重名冲突
|
||||
if ctx.Value(fsctx.DisableOverwrite) != nil {
|
||||
if util.Exists(dst) {
|
||||
util.Log().Warning("物理同名文件已存在或不可用: %s", dst)
|
||||
return errors.New("物理同名文件已存在或不可用")
|
||||
}
|
||||
}
|
||||
|
||||
// 如果目标目录不存在,创建
|
||||
basePath := filepath.Dir(dst)
|
||||
if !util.Exists(basePath) {
|
||||
|
|
|
@ -21,7 +21,8 @@ import (
|
|||
func TestHandler_Put(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
handler := Driver{}
|
||||
ctx := context.Background()
|
||||
ctx := context.WithValue(context.Background(), fsctx.DisableOverwrite, true)
|
||||
os.Remove(util.RelativePath("test/test/txt"))
|
||||
|
||||
testCases := []struct {
|
||||
file io.ReadCloser
|
||||
|
@ -33,6 +34,11 @@ func TestHandler_Put(t *testing.T) {
|
|||
dst: "test/test/txt",
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
file: ioutil.NopCloser(strings.NewReader("test input file")),
|
||||
dst: "test/test/txt",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
file: ioutil.NopCloser(strings.NewReader("test input file")),
|
||||
dst: "/notexist:/S.TXT",
|
||||
|
|
|
@ -235,8 +235,15 @@ func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, s
|
|||
// 凭证有效期
|
||||
credentialTTL := model.GetIntSetting("upload_credential_timeout", 3600)
|
||||
|
||||
// 是否允许覆盖
|
||||
overwrite := true
|
||||
if ctx.Value(fsctx.DisableOverwrite) != nil {
|
||||
overwrite = false
|
||||
}
|
||||
|
||||
options := []oss.Option{
|
||||
oss.Expires(time.Now().Add(time.Duration(credentialTTL) * time.Second)),
|
||||
oss.ForbidOverWrite(!overwrite),
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
|
|
|
@ -265,10 +265,11 @@ func TestDriver_Put(t *testing.T) {
|
|||
},
|
||||
}
|
||||
cache.Set("setting_upload_credential_timeout", "3600", 0)
|
||||
ctx := context.WithValue(context.Background(), fsctx.DisableOverwrite, true)
|
||||
|
||||
// 失败
|
||||
{
|
||||
err := handler.Put(context.Background(), ioutil.NopCloser(strings.NewReader("123")), "/123.txt", 3)
|
||||
err := handler.Put(ctx, ioutil.NopCloser(strings.NewReader("123")), "/123.txt", 3)
|
||||
asserts.Error(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,6 +155,13 @@ func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, s
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 决定是否要禁用文件覆盖
|
||||
overwrite := "true"
|
||||
if ctx.Value(fsctx.DisableOverwrite) != nil {
|
||||
overwrite = "false"
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
resp, err := handler.Client.Request(
|
||||
"POST",
|
||||
|
@ -164,6 +171,7 @@ func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, s
|
|||
"Authorization": {credential.Token},
|
||||
"X-Policy": {credential.Policy},
|
||||
"X-FileName": {fileName},
|
||||
"X-Overwrite": {overwrite},
|
||||
}),
|
||||
request.WithContentLength(int64(size)),
|
||||
request.WithTimeout(time.Duration(0)),
|
||||
|
@ -321,7 +329,8 @@ func (handler Driver) getUploadCredential(ctx context.Context, policy serializer
|
|||
// 签名上传策略
|
||||
uploadRequest, _ := http.NewRequest("POST", "/api/v3/slave/upload", nil)
|
||||
uploadRequest.Header = map[string][]string{
|
||||
"X-Policy": {policyEncoded},
|
||||
"X-Policy": {policyEncoded},
|
||||
"X-Overwrite": {"false"},
|
||||
}
|
||||
auth.SignRequest(handler.AuthInstance, uploadRequest, TTL)
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ func TestHandler_Token(t *testing.T) {
|
|||
},
|
||||
AuthInstance: auth.HMACAuth{},
|
||||
}
|
||||
ctx := context.Background()
|
||||
ctx := context.WithValue(context.Background(), fsctx.DisableOverwrite, true)
|
||||
auth.General = auth.HMACAuth{SecretKey: []byte("test")}
|
||||
|
||||
// 成功
|
||||
|
@ -49,6 +49,7 @@ func TestHandler_Token(t *testing.T) {
|
|||
asserts.Equal(true, policy.AutoRename)
|
||||
asserts.Equal("dir", policy.SavePath)
|
||||
asserts.Equal("file", policy.FileName)
|
||||
asserts.Equal("file", policy.FileName)
|
||||
asserts.Equal([]string{"txt"}, policy.AllowedExtension)
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ const (
|
|||
ShareKeyCtx
|
||||
// LimitParentCtx 限制父目录
|
||||
LimitParentCtx
|
||||
// IgnoreConflictCtx 忽略重名冲突
|
||||
IgnoreConflictCtx
|
||||
// IgnoreDirectoryConflictCtx 忽略目录重名冲突
|
||||
IgnoreDirectoryConflictCtx
|
||||
// RetryCtx 失败重试次数
|
||||
RetryCtx
|
||||
// ForceUsePublicEndpointCtx 强制使用公网 Endpoint
|
||||
|
@ -39,4 +39,6 @@ const (
|
|||
CancelFuncCtx
|
||||
// ValidateCapacityOnceCtx 限定归还容量的操作只執行一次
|
||||
ValidateCapacityOnceCtx
|
||||
// 禁止上传时同名覆盖操作
|
||||
DisableOverwrite
|
||||
)
|
||||
|
|
|
@ -403,8 +403,8 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, fullPath string) (*mo
|
|||
isExist, parent := fs.IsPathExist(base)
|
||||
if !isExist {
|
||||
// 递归创建父目录
|
||||
if _, ok := ctx.Value(fsctx.IgnoreConflictCtx).(bool); !ok {
|
||||
ctx = context.WithValue(ctx, fsctx.IgnoreConflictCtx, true)
|
||||
if _, ok := ctx.Value(fsctx.IgnoreDirectoryConflictCtx).(bool); !ok {
|
||||
ctx = context.WithValue(ctx, fsctx.IgnoreDirectoryConflictCtx, true)
|
||||
}
|
||||
newParent, err := fs.CreateDirectory(ctx, base)
|
||||
if err != nil {
|
||||
|
@ -427,7 +427,7 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, fullPath string) (*mo
|
|||
_, err := newFolder.Create()
|
||||
|
||||
if err != nil {
|
||||
if _, ok := ctx.Value(fsctx.IgnoreConflictCtx).(bool); !ok {
|
||||
if _, ok := ctx.Value(fsctx.IgnoreDirectoryConflictCtx).(bool); !ok {
|
||||
return nil, ErrFolderExisted
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
|
||||
)
|
||||
|
||||
// DecompressTask 文件压缩任务
|
||||
|
@ -81,7 +82,12 @@ func (job *DecompressTask) Do() {
|
|||
}
|
||||
|
||||
job.TaskModel.SetProgress(DecompressingProgress)
|
||||
err = fs.Decompress(context.Background(), job.TaskProps.Src, job.TaskProps.Dst)
|
||||
|
||||
// 禁止重名覆盖
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true)
|
||||
|
||||
err = fs.Decompress(ctx, job.TaskProps.Src, job.TaskProps.Dst)
|
||||
if err != nil {
|
||||
job.SetErrorMsg("解压缩失败", err)
|
||||
return
|
||||
|
|
|
@ -102,7 +102,7 @@ func (job *ImportTask) Do() {
|
|||
|
||||
// 列取目录、对象
|
||||
job.TaskModel.SetProgress(ListingProgress)
|
||||
coxIgnoreConflict := context.WithValue(context.Background(), fsctx.IgnoreConflictCtx,
|
||||
coxIgnoreConflict := context.WithValue(context.Background(), fsctx.IgnoreDirectoryConflictCtx,
|
||||
true)
|
||||
objects, err := fs.Handler.List(ctx, job.TaskProps.Src, job.TaskProps.Recursive)
|
||||
if err != nil {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
)
|
||||
|
||||
|
@ -102,7 +103,8 @@ func (job *TransferTask) Do() {
|
|||
dst = path.Join(job.TaskProps.Dst, strings.TrimPrefix(src, trim))
|
||||
}
|
||||
|
||||
err = fs.UploadFromPath(context.Background(), file, dst)
|
||||
ctx := context.WithValue(context.Background(), fsctx.DisableOverwrite, true)
|
||||
err = fs.UploadFromPath(ctx, file, dst)
|
||||
if err != nil {
|
||||
job.SetErrorMsg("文件转存失败", err)
|
||||
}
|
||||
|
|
|
@ -373,6 +373,9 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *filesyst
|
|||
fs.Use("AfterUploadFailed", filesystem.HookGiveBackCapacity)
|
||||
}
|
||||
|
||||
// 禁止覆盖
|
||||
ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true)
|
||||
|
||||
// 执行上传
|
||||
err = fs.Upload(ctx, fileData)
|
||||
if err != nil {
|
||||
|
@ -407,8 +410,8 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *filesy
|
|||
return http.StatusUnsupportedMediaType, nil
|
||||
}
|
||||
if strings.Contains(r.UserAgent(), "rclone") {
|
||||
if _, ok := ctx.Value(fsctx.IgnoreConflictCtx).(bool); !ok {
|
||||
ctx = context.WithValue(ctx, fsctx.IgnoreConflictCtx, true)
|
||||
if _, ok := ctx.Value(fsctx.IgnoreDirectoryConflictCtx).(bool); !ok {
|
||||
ctx = context.WithValue(ctx, fsctx.IgnoreDirectoryConflictCtx, true)
|
||||
}
|
||||
}
|
||||
if _, err := fs.CreateDirectory(ctx, reqPath); err != nil {
|
||||
|
|
|
@ -319,6 +319,7 @@ func FileUploadStream(c *gin.Context) {
|
|||
|
||||
// 执行上传
|
||||
ctx = context.WithValue(ctx, fsctx.ValidateCapacityOnceCtx, &sync.Once{})
|
||||
ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true)
|
||||
uploadCtx := context.WithValue(ctx, fsctx.GinCtx, c)
|
||||
err = fs.Upload(uploadCtx, fileData)
|
||||
if err != nil {
|
||||
|
|
|
@ -71,6 +71,11 @@ func SlaveUpload(c *gin.Context) {
|
|||
fs.Use("AfterUpload", filesystem.SlaveAfterUpload)
|
||||
fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile)
|
||||
|
||||
// 是否允许覆盖
|
||||
if c.Request.Header.Get("X-Overwrite") == "false" {
|
||||
ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true)
|
||||
}
|
||||
|
||||
// 执行上传
|
||||
err = fs.Upload(ctx, fileData)
|
||||
if err != nil {
|
||||
|
|
|
@ -78,6 +78,7 @@ func (service *SingleFileService) Create(c *gin.Context) serializer.Response {
|
|||
// 上下文
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true)
|
||||
|
||||
// 给文件系统分配钩子
|
||||
fs.Use("BeforeUpload", filesystem.HookValidateFile)
|
||||
|
|
Loading…
Add table
Reference in a new issue