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  }