github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/service/explorer/wopi.go (about) 1 package explorer 2 3 import ( 4 "errors" 5 "fmt" 6 "github.com/cloudreve/Cloudreve/v3/middleware" 7 model "github.com/cloudreve/Cloudreve/v3/models" 8 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" 9 "github.com/cloudreve/Cloudreve/v3/pkg/hashid" 10 "github.com/cloudreve/Cloudreve/v3/pkg/serializer" 11 "github.com/cloudreve/Cloudreve/v3/pkg/wopi" 12 "github.com/gin-gonic/gin" 13 "net/http" 14 "time" 15 ) 16 17 type WopiService struct { 18 } 19 20 func (service *WopiService) Rename(c *gin.Context) error { 21 fs, _, err := service.prepareFs(c) 22 if err != nil { 23 return err 24 } 25 26 defer fs.Recycle() 27 28 return fs.Rename(c, []uint{}, []uint{c.MustGet("object_id").(uint)}, c.GetHeader(wopi.RenameRequestHeader)) 29 } 30 31 func (service *WopiService) GetFile(c *gin.Context) error { 32 fs, _, err := service.prepareFs(c) 33 if err != nil { 34 return err 35 } 36 37 defer fs.Recycle() 38 39 resp, err := fs.Preview(c, fs.FileTarget[0].ID, true) 40 if err != nil { 41 return fmt.Errorf("failed to pull file content: %w", err) 42 } 43 44 // 重定向到文件源 45 if resp.Redirect { 46 return fmt.Errorf("redirect not supported in WOPI") 47 } 48 49 // 直接返回文件内容 50 defer resp.Content.Close() 51 52 c.Header("Cache-Control", "no-cache") 53 http.ServeContent(c.Writer, c.Request, fs.FileTarget[0].Name, fs.FileTarget[0].UpdatedAt, resp.Content) 54 return nil 55 } 56 57 func (service *WopiService) FileInfo(c *gin.Context) (*serializer.WopiFileInfo, error) { 58 fs, session, err := service.prepareFs(c) 59 if err != nil { 60 return nil, err 61 } 62 63 defer fs.Recycle() 64 65 parent, err := model.GetFoldersByIDs([]uint{fs.FileTarget[0].FolderID}, fs.User.ID) 66 if err != nil { 67 return nil, err 68 } 69 70 if len(parent) == 0 { 71 return nil, fmt.Errorf("failed to find parent folder") 72 } 73 74 parent[0].TraceRoot() 75 siteUrl := model.GetSiteURL() 76 77 // Generate url for parent folder 78 parentUrl := model.GetSiteURL() 79 parentUrl.Path = "/home" 80 query := parentUrl.Query() 81 query.Set("path", parent[0].Position) 82 parentUrl.RawQuery = query.Encode() 83 84 info := &serializer.WopiFileInfo{ 85 BaseFileName: fs.FileTarget[0].Name, 86 Version: fs.FileTarget[0].Model.UpdatedAt.String(), 87 BreadcrumbBrandName: model.GetSettingByName("siteName"), 88 BreadcrumbBrandUrl: siteUrl.String(), 89 FileSharingPostMessage: false, 90 PostMessageOrigin: "*", 91 FileNameMaxLength: 256, 92 LastModifiedTime: fs.FileTarget[0].Model.UpdatedAt.Format(time.RFC3339), 93 IsAnonymousUser: true, 94 ReadOnly: true, 95 ClosePostMessage: true, 96 Size: int64(fs.FileTarget[0].Size), 97 OwnerId: hashid.HashID(fs.FileTarget[0].UserID, hashid.UserID), 98 } 99 100 if session.Action == wopi.ActionEdit { 101 info.FileSharingPostMessage = true 102 info.IsAnonymousUser = false 103 info.SupportsRename = true 104 info.SupportsReviewing = true 105 info.SupportsUpdate = true 106 info.UserFriendlyName = fs.User.Nick 107 info.UserId = hashid.HashID(fs.User.ID, hashid.UserID) 108 info.UserCanRename = true 109 info.UserCanReview = true 110 info.UserCanWrite = true 111 info.ReadOnly = false 112 info.BreadcrumbFolderName = parent[0].Name 113 info.BreadcrumbFolderUrl = parentUrl.String() 114 } 115 116 return info, nil 117 } 118 119 func (service *WopiService) prepareFs(c *gin.Context) (*filesystem.FileSystem, *wopi.SessionCache, error) { 120 // 创建文件系统 121 fs, err := filesystem.NewFileSystemFromContext(c) 122 if err != nil { 123 return nil, nil, err 124 } 125 126 session := c.MustGet(middleware.WopiSessionCtx).(*wopi.SessionCache) 127 if err := fs.SetTargetFileByIDs([]uint{session.FileID}); err != nil { 128 fs.Recycle() 129 return nil, nil, fmt.Errorf("failed to find file: %w", err) 130 } 131 132 maxSize := model.GetIntSetting("maxEditSize", 0) 133 if maxSize > 0 && fs.FileTarget[0].Size > uint64(maxSize) { 134 return nil, nil, errors.New("file too large") 135 } 136 137 return fs, session, nil 138 }