Feat: disable overwrite for non-updating put request, only works under local,slave,OneDrive,OSS policy. (#764)

feat-album
HFO4 3 years ago
parent c949d47161
commit 5e226efea1

@ -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…
Cancel
Save