package explorer import ( "errors" "fmt" "github.com/cloudreve/Cloudreve/v3/middleware" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" "github.com/cloudreve/Cloudreve/v3/pkg/hashid" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/pkg/wopi" "github.com/gin-gonic/gin" "net/http" "time" ) type WopiService struct { } func (service *WopiService) Rename(c *gin.Context) error { fs, _, err := service.prepareFs(c) if err != nil { return err } defer fs.Recycle() return fs.Rename(c, []uint{}, []uint{c.MustGet("object_id").(uint)}, c.GetHeader(wopi.RenameRequestHeader)) } func (service *WopiService) GetFile(c *gin.Context) error { fs, _, err := service.prepareFs(c) if err != nil { return err } defer fs.Recycle() resp, err := fs.Preview(c, fs.FileTarget[0].ID, true) if err != nil { return fmt.Errorf("failed to pull file content: %w", err) } // 重定向到文件源 if resp.Redirect { return fmt.Errorf("redirect not supported in WOPI") } // 直接返回文件内容 defer resp.Content.Close() c.Header("Cache-Control", "no-cache") http.ServeContent(c.Writer, c.Request, fs.FileTarget[0].Name, fs.FileTarget[0].UpdatedAt, resp.Content) return nil } func (service *WopiService) FileInfo(c *gin.Context) (*serializer.WopiFileInfo, error) { fs, session, err := service.prepareFs(c) if err != nil { return nil, err } defer fs.Recycle() parent, err := model.GetFoldersByIDs([]uint{fs.FileTarget[0].FolderID}, fs.User.ID) if err != nil { return nil, err } if len(parent) == 0 { return nil, fmt.Errorf("failed to find parent folder") } parent[0].TraceRoot() siteUrl := model.GetSiteURL() // Generate url for parent folder parentUrl := model.GetSiteURL() parentUrl.Path = "/home" query := parentUrl.Query() query.Set("path", parent[0].Position) parentUrl.RawQuery = query.Encode() info := &serializer.WopiFileInfo{ BaseFileName: fs.FileTarget[0].Name, Version: fs.FileTarget[0].Model.UpdatedAt.String(), BreadcrumbBrandName: model.GetSettingByName("siteName"), BreadcrumbBrandUrl: siteUrl.String(), FileSharingPostMessage: false, PostMessageOrigin: "*", FileNameMaxLength: 256, LastModifiedTime: fs.FileTarget[0].Model.UpdatedAt.Format(time.RFC3339), IsAnonymousUser: true, ReadOnly: true, ClosePostMessage: true, Size: int64(fs.FileTarget[0].Size), OwnerId: hashid.HashID(fs.FileTarget[0].UserID, hashid.UserID), } if session.Action == wopi.ActionEdit { info.FileSharingPostMessage = true info.IsAnonymousUser = false info.SupportsRename = true info.SupportsReviewing = true info.SupportsUpdate = true info.UserFriendlyName = fs.User.Nick info.UserId = hashid.HashID(fs.User.ID, hashid.UserID) info.UserCanRename = true info.UserCanReview = true info.UserCanWrite = true info.ReadOnly = false info.BreadcrumbFolderName = parent[0].Name info.BreadcrumbFolderUrl = parentUrl.String() } return info, nil } func (service *WopiService) prepareFs(c *gin.Context) (*filesystem.FileSystem, *wopi.SessionCache, error) { // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { return nil, nil, err } session := c.MustGet(middleware.WopiSessionCtx).(*wopi.SessionCache) if err := fs.SetTargetFileByIDs([]uint{session.FileID}); err != nil { fs.Recycle() return nil, nil, fmt.Errorf("failed to find file: %w", err) } maxSize := model.GetIntSetting("maxEditSize", 0) if maxSize > 0 && fs.FileTarget[0].Size > uint64(maxSize) { return nil, nil, errors.New("file too large") } return fs, session, nil }