mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-11-01 00:57:15 +08:00 
			
		
		
		
	Feat: support apply append mode and overwrite mode for FileStream
This commit is contained in:
		
							
								
								
									
										2
									
								
								assets
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								assets
									
									
									
									
									
								
							 Submodule assets updated: b90a49ba5d...eb3f32922a
									
								
							| @ -308,7 +308,7 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst string) error { | |||||||
| 					Size:        uint64(size), | 					Size:        uint64(size), | ||||||
| 					Name:        path.Base(dst), | 					Name:        path.Base(dst), | ||||||
| 					VirtualPath: path.Dir(dst), | 					VirtualPath: path.Dir(dst), | ||||||
| 					Mode:        fsctx.Create, | 					Mode:        0, | ||||||
| 				}) | 				}) | ||||||
| 				fileStream.Close() | 				fileStream.Close() | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
|  | |||||||
| @ -93,7 +93,7 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { | |||||||
| 	dst := util.RelativePath(filepath.FromSlash(fileInfo.SavePath)) | 	dst := util.RelativePath(filepath.FromSlash(fileInfo.SavePath)) | ||||||
|  |  | ||||||
| 	// 如果非 Overwrite,则检查是否有重名冲突 | 	// 如果非 Overwrite,则检查是否有重名冲突 | ||||||
| 	if fileInfo.Mode == fsctx.Create { | 	if fileInfo.Mode&fsctx.Overwrite != fsctx.Overwrite { | ||||||
| 		if util.Exists(dst) { | 		if util.Exists(dst) { | ||||||
| 			util.Log().Warning("物理同名文件已存在或不可用: %s", dst) | 			util.Log().Warning("物理同名文件已存在或不可用: %s", dst) | ||||||
| 			return errors.New("物理同名文件已存在或不可用") | 			return errors.New("物理同名文件已存在或不可用") | ||||||
| @ -115,21 +115,21 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { | |||||||
| 		err error | 		err error | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	if fileInfo.Mode == fsctx.Append { | 	openMode := os.O_CREATE | os.O_RDWR | ||||||
| 		// 如果是追加模式,则直接打开文件 | 	if fileInfo.Mode&fsctx.Append == fsctx.Append { | ||||||
| 		out, err = os.OpenFile(dst, os.O_APPEND|os.O_CREATE|os.O_WRONLY, Perm) | 		openMode |= os.O_APPEND | ||||||
| 	} else { | 	} else { | ||||||
| 		// 创建或覆盖目标文件 | 		openMode |= os.O_TRUNC | ||||||
| 		out, err = os.Create(dst) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	out, err = os.OpenFile(dst, openMode, Perm) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		util.Log().Warning("无法打开或创建文件,%s", err) | 		util.Log().Warning("无法打开或创建文件,%s", err) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	defer out.Close() | 	defer out.Close() | ||||||
|  |  | ||||||
| 	if fileInfo.Mode == fsctx.Append { | 	if fileInfo.Mode&fsctx.Append == fsctx.Append { | ||||||
| 		stat, err := out.Stat() | 		stat, err := out.Stat() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			util.Log().Warning("无法读取文件信息,%s", err) | 			util.Log().Warning("无法读取文件信息,%s", err) | ||||||
| @ -144,7 +144,7 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { | |||||||
| 				return fmt.Errorf("覆盖分片时发生错误: %w", err) | 				return fmt.Errorf("覆盖分片时发生错误: %w", err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			out, err = os.OpenFile(dst, os.O_APPEND|os.O_CREATE|os.O_WRONLY, Perm) | 			out, err = os.OpenFile(dst, openMode, Perm) | ||||||
| 			defer out.Close() | 			defer out.Close() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				util.Log().Warning("无法打开或创建文件,%s", err) | 				util.Log().Warning("无法打开或创建文件,%s", err) | ||||||
|  | |||||||
| @ -260,9 +260,9 @@ func (client *Client) UploadChunk(ctx context.Context, uploadURL string, chunk * | |||||||
| func (client *Client) Upload(ctx context.Context, file fsctx.FileHeader) error { | func (client *Client) Upload(ctx context.Context, file fsctx.FileHeader) error { | ||||||
| 	fileInfo := file.Info() | 	fileInfo := file.Info() | ||||||
| 	// 决定是否覆盖文件 | 	// 决定是否覆盖文件 | ||||||
| 	overwrite := "replace" | 	overwrite := "fail" | ||||||
| 	if fileInfo.Mode != fsctx.Create { | 	if fileInfo.Mode&fsctx.Overwrite == fsctx.Overwrite { | ||||||
| 		overwrite = "fail" | 		overwrite = "replace" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	size := int(fileInfo.Size) | 	size := int(fileInfo.Size) | ||||||
|  | |||||||
| @ -237,11 +237,7 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { | |||||||
| 	credentialTTL := model.GetIntSetting("upload_credential_timeout", 3600) | 	credentialTTL := model.GetIntSetting("upload_credential_timeout", 3600) | ||||||
|  |  | ||||||
| 	// 是否允许覆盖 | 	// 是否允许覆盖 | ||||||
| 	overwrite := true | 	overwrite := fileInfo.Mode&fsctx.Overwrite == fsctx.Overwrite | ||||||
| 	if fileInfo.Mode == fsctx.Create { |  | ||||||
| 		overwrite = false |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	options := []oss.Option{ | 	options := []oss.Option{ | ||||||
| 		oss.Expires(time.Now().Add(time.Duration(credentialTTL) * time.Second)), | 		oss.Expires(time.Now().Add(time.Duration(credentialTTL) * time.Second)), | ||||||
| 		oss.ForbidOverWrite(!overwrite), | 		oss.ForbidOverWrite(!overwrite), | ||||||
|  | |||||||
| @ -173,9 +173,9 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { | |||||||
| 	fileName := url.QueryEscape(path.Base(fileInfo.SavePath)) | 	fileName := url.QueryEscape(path.Base(fileInfo.SavePath)) | ||||||
|  |  | ||||||
| 	// 决定是否要禁用文件覆盖 | 	// 决定是否要禁用文件覆盖 | ||||||
| 	overwrite := "true" | 	overwrite := "false" | ||||||
| 	if fileInfo.Mode != fsctx.Create { | 	if fileInfo.Mode&fsctx.Overwrite == fsctx.Overwrite { | ||||||
| 		overwrite = "false" | 		overwrite = "true" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 上传文件 | 	// 上传文件 | ||||||
|  | |||||||
| @ -8,11 +8,10 @@ import ( | |||||||
| type WriteMode int | type WriteMode int | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	Overwrite WriteMode = iota | 	Overwrite WriteMode = 0x00001 | ||||||
| 	// Append 只适用于本地策略 | 	// Append 只适用于本地策略 | ||||||
| 	Append | 	Append = 0x00002 | ||||||
| 	Create | 	Nop    = 0x00004 | ||||||
| 	Nop |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type UploadTaskInfo struct { | type UploadTaskInfo struct { | ||||||
|  | |||||||
| @ -151,6 +151,7 @@ func HookCleanFileContent(ctx context.Context, fs *FileSystem, file fsctx.FileHe | |||||||
| 		File:     ioutil.NopCloser(strings.NewReader("")), | 		File:     ioutil.NopCloser(strings.NewReader("")), | ||||||
| 		SavePath: file.Info().SavePath, | 		SavePath: file.Info().SavePath, | ||||||
| 		Size:     0, | 		Size:     0, | ||||||
|  | 		Model:    fsctx.Overwrite, | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ func (fs *FileSystem) Upload(ctx context.Context, file *fsctx.FileStream) (err e | |||||||
| 	go fs.CancelUpload(ctx, savePath, file) | 	go fs.CancelUpload(ctx, savePath, file) | ||||||
|  |  | ||||||
| 	// 保存文件 | 	// 保存文件 | ||||||
| 	if file.Mode != fsctx.Nop { | 	if file.Mode&fsctx.Nop != fsctx.Nop { | ||||||
| 		err = fs.Handler.Put(ctx, file) | 		err = fs.Handler.Put(ctx, file) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			fs.Trigger(ctx, "AfterUploadFailed", file) | 			fs.Trigger(ctx, "AfterUploadFailed", file) | ||||||
| @ -73,7 +73,7 @@ func (fs *FileSystem) Upload(ctx context.Context, file *fsctx.FileStream) (err e | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if file.Mode == fsctx.Create { | 	if file.Mode&fsctx.Overwrite == 0 { | ||||||
| 		fileInfo := file.Info() | 		fileInfo := file.Info() | ||||||
| 		util.Log().Info( | 		util.Log().Info( | ||||||
| 			"新文件PUT:%s , 大小:%d, 上传者:%s", | 			"新文件PUT:%s , 大小:%d, 上传者:%s", | ||||||
|  | |||||||
| @ -7,7 +7,6 @@ import ( | |||||||
|  |  | ||||||
| 	model "github.com/cloudreve/Cloudreve/v3/models" | 	model "github.com/cloudreve/Cloudreve/v3/models" | ||||||
| 	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem" | 	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem" | ||||||
| 	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" |  | ||||||
| 	"github.com/cloudreve/Cloudreve/v3/pkg/util" | 	"github.com/cloudreve/Cloudreve/v3/pkg/util" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @ -107,7 +106,7 @@ func (job *CompressTask) Do() { | |||||||
| 	job.TaskModel.SetProgress(TransferringProgress) | 	job.TaskModel.SetProgress(TransferringProgress) | ||||||
|  |  | ||||||
| 	// 上传文件 | 	// 上传文件 | ||||||
| 	err = fs.UploadFromPath(ctx, zipFile, job.TaskProps.Dst, true, fsctx.Create) | 	err = fs.UploadFromPath(ctx, zipFile, job.TaskProps.Dst, true, 0) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		job.SetErrorMsg(err.Error()) | 		job.SetErrorMsg(err.Error()) | ||||||
| 		return | 		return | ||||||
|  | |||||||
| @ -119,7 +119,6 @@ func (job *TransferTask) Do() { | |||||||
|  |  | ||||||
| 	err = fs.Handler.Put(context.Background(), &fsctx.FileStream{ | 	err = fs.Handler.Put(context.Background(), &fsctx.FileStream{ | ||||||
| 		File:     file, | 		File:     file, | ||||||
| 		Mode:     fsctx.Create, |  | ||||||
| 		SavePath: job.Req.Dst, | 		SavePath: job.Req.Dst, | ||||||
| 		Size:     uint64(size), | 		Size:     uint64(size), | ||||||
| 	}) | 	}) | ||||||
|  | |||||||
| @ -123,11 +123,10 @@ func (job *TransferTask) Do() { | |||||||
| 				Size:        job.TaskProps.SrcSizes[file], | 				Size:        job.TaskProps.SrcSizes[file], | ||||||
| 				Name:        path.Base(dst), | 				Name:        path.Base(dst), | ||||||
| 				VirtualPath: path.Dir(dst), | 				VirtualPath: path.Dir(dst), | ||||||
| 				Mode:        fsctx.Create, |  | ||||||
| 			}) | 			}) | ||||||
| 		} else { | 		} else { | ||||||
| 			// 主机节点中转 | 			// 主机节点中转 | ||||||
| 			err = fs.UploadFromPath(context.Background(), file, dst, true, fsctx.Create) | 			err = fs.UploadFromPath(context.Background(), file, dst, true, 0) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | |||||||
| @ -355,6 +355,7 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *filesyst | |||||||
| 		fs.Use("AfterValidateFailed", filesystem.HookCleanFileContent) | 		fs.Use("AfterValidateFailed", filesystem.HookCleanFileContent) | ||||||
| 		fs.Use("AfterValidateFailed", filesystem.HookClearFileSize) | 		fs.Use("AfterValidateFailed", filesystem.HookClearFileSize) | ||||||
| 		ctx = context.WithValue(ctx, fsctx.FileModelCtx, *originFile) | 		ctx = context.WithValue(ctx, fsctx.FileModelCtx, *originFile) | ||||||
|  | 		fileData.Mode |= fsctx.Overwrite | ||||||
| 	} else { | 	} else { | ||||||
| 		// 给文件系统分配钩子 | 		// 给文件系统分配钩子 | ||||||
| 		fs.Use("BeforeUpload", filesystem.HookValidateFile) | 		fs.Use("BeforeUpload", filesystem.HookValidateFile) | ||||||
| @ -364,9 +365,6 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *filesyst | |||||||
| 		fs.Use("AfterUpload", filesystem.GenericAfterUpload) | 		fs.Use("AfterUpload", filesystem.GenericAfterUpload) | ||||||
| 		fs.Use("AfterUpload", filesystem.HookGenerateThumb) | 		fs.Use("AfterUpload", filesystem.HookGenerateThumb) | ||||||
| 		fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile) | 		fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile) | ||||||
|  |  | ||||||
| 		// 禁止覆盖 |  | ||||||
| 		fileData.Mode = fsctx.Create |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 执行上传 | 	// 执行上传 | ||||||
|  | |||||||
| @ -73,10 +73,10 @@ func SlaveUpload(c *gin.Context) { | |||||||
| 	fs.Use("AfterUpload", filesystem.SlaveAfterUpload) | 	fs.Use("AfterUpload", filesystem.SlaveAfterUpload) | ||||||
| 	fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile) | 	fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile) | ||||||
|  |  | ||||||
| 	// 是否允许覆盖 | 	//// 是否允许覆盖 | ||||||
| 	if c.Request.Header.Get("X-Cr-Overwrite") == "false" { | 	//if c.Request.Header.Get("X-Cr-Overwrite") == "false" { | ||||||
| 		fileData.Mode = fsctx.Create | 	//	fileData.Mode = fsctx.Create | ||||||
| 	} | 	//} | ||||||
|  |  | ||||||
| 	// 执行上传 | 	// 执行上传 | ||||||
| 	err = fs.Upload(ctx, &fileData) | 	err = fs.Upload(ctx, &fileData) | ||||||
|  | |||||||
| @ -65,7 +65,6 @@ func (service *SingleFileService) Create(c *gin.Context) serializer.Response { | |||||||
| 		Size:        0, | 		Size:        0, | ||||||
| 		VirtualPath: path.Dir(service.Path), | 		VirtualPath: path.Dir(service.Path), | ||||||
| 		Name:        path.Base(service.Path), | 		Name:        path.Base(service.Path), | ||||||
| 		Mode:        fsctx.Create, |  | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return serializer.Err(serializer.CodeUploadFailed, err.Error(), err) | 		return serializer.Err(serializer.CodeUploadFailed, err.Error(), err) | ||||||
| @ -375,6 +374,7 @@ func (service *FileIDService) PutContent(ctx context.Context, c *gin.Context) se | |||||||
| 		MIMEType: c.Request.Header.Get("Content-Type"), | 		MIMEType: c.Request.Header.Get("Content-Type"), | ||||||
| 		File:     c.Request.Body, | 		File:     c.Request.Body, | ||||||
| 		Size:     fileSize, | 		Size:     fileSize, | ||||||
|  | 		Model:    fsctx.Overwrite, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 创建文件系统 | 	// 创建文件系统 | ||||||
|  | |||||||
| @ -148,7 +148,7 @@ func processChunkUpload(ctx context.Context, c *gin.Context, fs *filesystem.File | |||||||
| 		Name:         session.Name, | 		Name:         session.Name, | ||||||
| 		VirtualPath:  session.VirtualPath, | 		VirtualPath:  session.VirtualPath, | ||||||
| 		SavePath:     session.SavePath, | 		SavePath:     session.SavePath, | ||||||
| 		Mode:         fsctx.Append, | 		Mode:         fsctx.Append | fsctx.Overwrite, | ||||||
| 		AppendStart:  chunkSize * uint64(index), | 		AppendStart:  chunkSize * uint64(index), | ||||||
| 		Model:        file, | 		Model:        file, | ||||||
| 		LastModified: session.LastModified, | 		LastModified: session.LastModified, | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 HFO4
					HFO4