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  }