github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/service/callback/upload.go (about) 1 package callback 2 3 import ( 4 "context" 5 "fmt" 6 model "github.com/cloudreve/Cloudreve/v3/models" 7 "strings" 8 9 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" 10 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/cos" 11 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/onedrive" 12 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/s3" 13 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" 14 "github.com/cloudreve/Cloudreve/v3/pkg/serializer" 15 "github.com/gin-gonic/gin" 16 ) 17 18 // CallbackProcessService 上传请求回调正文接口 19 type CallbackProcessService interface { 20 GetBody() serializer.UploadCallback 21 } 22 23 // RemoteUploadCallbackService 远程存储上传回调请求服务 24 type RemoteUploadCallbackService struct { 25 Data serializer.UploadCallback `json:"data" binding:"required"` 26 } 27 28 // GetBody 返回回调正文 29 func (service RemoteUploadCallbackService) GetBody() serializer.UploadCallback { 30 return service.Data 31 } 32 33 // UploadCallbackService OOS/七牛云存储上传回调请求服务 34 type UploadCallbackService struct { 35 Name string `json:"name"` 36 SourceName string `json:"source_name"` 37 PicInfo string `json:"pic_info"` 38 Size uint64 `json:"size"` 39 } 40 41 // UpyunCallbackService 又拍云上传回调请求服务 42 type UpyunCallbackService struct { 43 Code int `form:"code" binding:"required"` 44 Message string `form:"message" binding:"required"` 45 SourceName string `form:"url" binding:"required"` 46 Width string `form:"image-width"` 47 Height string `form:"image-height"` 48 Size uint64 `form:"file_size"` 49 } 50 51 // OneDriveCallback OneDrive 客户端回调正文 52 type OneDriveCallback struct { 53 Meta *onedrive.FileInfo 54 } 55 56 // COSCallback COS 客户端回调正文 57 type COSCallback struct { 58 Bucket string `form:"bucket"` 59 Etag string `form:"etag"` 60 } 61 62 // S3Callback S3 客户端回调正文 63 type S3Callback struct { 64 } 65 66 // GetBody 返回回调正文 67 func (service UpyunCallbackService) GetBody() serializer.UploadCallback { 68 res := serializer.UploadCallback{} 69 if service.Width != "" { 70 res.PicInfo = service.Width + "," + service.Height 71 } 72 73 return res 74 } 75 76 // GetBody 返回回调正文 77 func (service UploadCallbackService) GetBody() serializer.UploadCallback { 78 return serializer.UploadCallback{ 79 PicInfo: service.PicInfo, 80 } 81 } 82 83 // GetBody 返回回调正文 84 func (service OneDriveCallback) GetBody() serializer.UploadCallback { 85 var picInfo = "0,0" 86 if service.Meta.Image.Width != 0 { 87 picInfo = fmt.Sprintf("%d,%d", service.Meta.Image.Width, service.Meta.Image.Height) 88 } 89 return serializer.UploadCallback{ 90 PicInfo: picInfo, 91 } 92 } 93 94 // GetBody 返回回调正文 95 func (service COSCallback) GetBody() serializer.UploadCallback { 96 return serializer.UploadCallback{ 97 PicInfo: "", 98 } 99 } 100 101 // GetBody 返回回调正文 102 func (service S3Callback) GetBody() serializer.UploadCallback { 103 return serializer.UploadCallback{ 104 PicInfo: "", 105 } 106 } 107 108 // ProcessCallback 处理上传结果回调 109 func ProcessCallback(service CallbackProcessService, c *gin.Context) serializer.Response { 110 callbackBody := service.GetBody() 111 112 // 创建文件系统 113 fs, err := filesystem.NewFileSystemFromCallback(c) 114 if err != nil { 115 return serializer.Err(serializer.CodeCreateFSError, err.Error(), err) 116 } 117 defer fs.Recycle() 118 119 // 获取上传会话 120 uploadSession := c.MustGet(filesystem.UploadSessionCtx).(*serializer.UploadSession) 121 122 // 查找上传会话创建的占位文件 123 file, err := model.GetFilesByUploadSession(uploadSession.Key, fs.User.ID) 124 if err != nil { 125 return serializer.Err(serializer.CodeUploadSessionExpired, "LocalUpload session file placeholder not exist", err) 126 } 127 128 fileData := fsctx.FileStream{ 129 Size: uploadSession.Size, 130 Name: uploadSession.Name, 131 VirtualPath: uploadSession.VirtualPath, 132 SavePath: uploadSession.SavePath, 133 Mode: fsctx.Nop, 134 Model: file, 135 LastModified: uploadSession.LastModified, 136 } 137 138 // 占位符未扣除容量需要校验和扣除 139 if !fs.Policy.IsUploadPlaceholderWithSize() { 140 fs.Use("AfterUpload", filesystem.HookValidateCapacity) 141 fs.Use("AfterUpload", filesystem.HookChunkUploaded) 142 } 143 144 fs.Use("AfterUpload", filesystem.HookPopPlaceholderToFile(callbackBody.PicInfo)) 145 fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile) 146 err = fs.Upload(context.Background(), &fileData) 147 if err != nil { 148 return serializer.Err(serializer.CodeUploadFailed, err.Error(), err) 149 } 150 151 return serializer.Response{} 152 } 153 154 // PreProcess 对OneDrive客户端回调进行预处理验证 155 func (service *OneDriveCallback) PreProcess(c *gin.Context) serializer.Response { 156 // 创建文件系统 157 fs, err := filesystem.NewFileSystemFromCallback(c) 158 if err != nil { 159 return serializer.Err(serializer.CodeCreateFSError, "", err) 160 } 161 defer fs.Recycle() 162 163 // 获取回调会话 164 uploadSession := c.MustGet(filesystem.UploadSessionCtx).(*serializer.UploadSession) 165 166 // 获取文件信息 167 info, err := fs.Handler.(onedrive.Driver).Client.Meta(context.Background(), "", uploadSession.SavePath) 168 if err != nil { 169 return serializer.Err(serializer.CodeQueryMetaFailed, "", err) 170 } 171 172 // 验证与回调会话中是否一致 173 actualPath := strings.TrimPrefix(uploadSession.SavePath, "/") 174 isSizeCheckFailed := uploadSession.Size != info.Size 175 176 // SharePoint 会对 Office 文档增加 meta data 导致文件大小不一致,这里增加 1 MB 宽容 177 // See: https://github.com/OneDrive/onedrive-api-docs/issues/935 178 if (strings.Contains(fs.Policy.OptionsSerialized.OdDriver, "sharepoint.com") || strings.Contains(fs.Policy.OptionsSerialized.OdDriver, "sharepoint.cn")) && isSizeCheckFailed && (info.Size > uploadSession.Size) && (info.Size-uploadSession.Size <= 1048576) { 179 isSizeCheckFailed = false 180 } 181 182 if isSizeCheckFailed || !strings.EqualFold(info.GetSourcePath(), actualPath) { 183 fs.Handler.(onedrive.Driver).Client.Delete(context.Background(), []string{info.GetSourcePath()}) 184 return serializer.Err(serializer.CodeMetaMismatch, "", err) 185 } 186 service.Meta = info 187 return ProcessCallback(service, c) 188 } 189 190 // PreProcess 对COS客户端回调进行预处理 191 func (service *COSCallback) PreProcess(c *gin.Context) serializer.Response { 192 // 创建文件系统 193 fs, err := filesystem.NewFileSystemFromCallback(c) 194 if err != nil { 195 return serializer.Err(serializer.CodeCreateFSError, "", err) 196 } 197 defer fs.Recycle() 198 199 // 获取回调会话 200 uploadSession := c.MustGet(filesystem.UploadSessionCtx).(*serializer.UploadSession) 201 202 // 获取文件信息 203 info, err := fs.Handler.(cos.Driver).Meta(context.Background(), uploadSession.SavePath) 204 if err != nil { 205 return serializer.Err(serializer.CodeMetaMismatch, "", err) 206 } 207 208 // 验证实际文件信息与回调会话中是否一致 209 if uploadSession.Size != info.Size || uploadSession.Key != info.CallbackKey { 210 return serializer.Err(serializer.CodeMetaMismatch, "", err) 211 } 212 213 return ProcessCallback(service, c) 214 } 215 216 // PreProcess 对S3客户端回调进行预处理 217 func (service *S3Callback) PreProcess(c *gin.Context) serializer.Response { 218 // 创建文件系统 219 fs, err := filesystem.NewFileSystemFromCallback(c) 220 if err != nil { 221 return serializer.Err(serializer.CodeCreateFSError, "", err) 222 } 223 defer fs.Recycle() 224 225 // 获取回调会话 226 uploadSession := c.MustGet(filesystem.UploadSessionCtx).(*serializer.UploadSession) 227 228 // 获取文件信息 229 info, err := fs.Handler.(*s3.Driver).Meta(context.Background(), uploadSession.SavePath) 230 if err != nil { 231 return serializer.Err(serializer.CodeMetaMismatch, "", err) 232 } 233 234 // 验证实际文件信息与回调会话中是否一致 235 if uploadSession.Size != info.Size { 236 return serializer.Err(serializer.CodeMetaMismatch, "", err) 237 } 238 239 return ProcessCallback(service, c) 240 } 241 242 // PreProcess 对从机客户端回调进行预处理验证 243 func (service *UploadCallbackService) PreProcess(c *gin.Context) serializer.Response { 244 // 创建文件系统 245 fs, err := filesystem.NewFileSystemFromCallback(c) 246 if err != nil { 247 return serializer.Err(serializer.CodeCreateFSError, "", err) 248 } 249 defer fs.Recycle() 250 251 // 获取回调会话 252 uploadSession := c.MustGet(filesystem.UploadSessionCtx).(*serializer.UploadSession) 253 254 // 验证文件大小 255 if uploadSession.Size != service.Size { 256 fs.Handler.Delete(context.Background(), []string{uploadSession.SavePath}) 257 return serializer.Err(serializer.CodeMetaMismatch, "", err) 258 } 259 260 return ProcessCallback(service, c) 261 }