github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/filesystem/driver/onedrive/handler.go (about)

     1  package onedrive
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/url"
     8  	"path"
     9  	"path/filepath"
    10  	"strings"
    11  	"time"
    12  
    13  	model "github.com/cloudreve/Cloudreve/v3/models"
    14  	"github.com/cloudreve/Cloudreve/v3/pkg/cache"
    15  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver"
    16  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
    17  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/response"
    18  	"github.com/cloudreve/Cloudreve/v3/pkg/request"
    19  	"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
    20  )
    21  
    22  // Driver OneDrive 适配器
    23  type Driver struct {
    24  	Policy     *model.Policy
    25  	Client     *Client
    26  	HTTPClient request.Client
    27  }
    28  
    29  // NewDriver 从存储策略初始化新的Driver实例
    30  func NewDriver(policy *model.Policy) (driver.Handler, error) {
    31  	client, err := NewClient(policy)
    32  	if policy.OptionsSerialized.ChunkSize == 0 {
    33  		policy.OptionsSerialized.ChunkSize = 50 << 20 // 50MB
    34  	}
    35  
    36  	return Driver{
    37  		Policy:     policy,
    38  		Client:     client,
    39  		HTTPClient: request.NewClient(),
    40  	}, err
    41  }
    42  
    43  // List 列取项目
    44  func (handler Driver) List(ctx context.Context, base string, recursive bool) ([]response.Object, error) {
    45  	base = strings.TrimPrefix(base, "/")
    46  	// 列取子项目
    47  	objects, _ := handler.Client.ListChildren(ctx, base)
    48  
    49  	// 获取真实的列取起始根目录
    50  	rootPath := base
    51  	if realBase, ok := ctx.Value(fsctx.PathCtx).(string); ok {
    52  		rootPath = realBase
    53  	} else {
    54  		ctx = context.WithValue(ctx, fsctx.PathCtx, base)
    55  	}
    56  
    57  	// 整理结果
    58  	res := make([]response.Object, 0, len(objects))
    59  	for _, object := range objects {
    60  		source := path.Join(base, object.Name)
    61  		rel, err := filepath.Rel(rootPath, source)
    62  		if err != nil {
    63  			continue
    64  		}
    65  		res = append(res, response.Object{
    66  			Name:         object.Name,
    67  			RelativePath: filepath.ToSlash(rel),
    68  			Source:       source,
    69  			Size:         object.Size,
    70  			IsDir:        object.Folder != nil,
    71  			LastModify:   time.Now(),
    72  		})
    73  	}
    74  
    75  	// 递归列取子目录
    76  	if recursive {
    77  		for _, object := range objects {
    78  			if object.Folder != nil {
    79  				sub, _ := handler.List(ctx, path.Join(base, object.Name), recursive)
    80  				res = append(res, sub...)
    81  			}
    82  		}
    83  	}
    84  
    85  	return res, nil
    86  }
    87  
    88  // Get 获取文件
    89  func (handler Driver) Get(ctx context.Context, path string) (response.RSCloser, error) {
    90  	// 获取文件源地址
    91  	downloadURL, err := handler.Source(
    92  		ctx,
    93  		path,
    94  		60,
    95  		false,
    96  		0,
    97  	)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	// 获取文件数据流
   103  	resp, err := handler.HTTPClient.Request(
   104  		"GET",
   105  		downloadURL,
   106  		nil,
   107  		request.WithContext(ctx),
   108  		request.WithTimeout(time.Duration(0)),
   109  	).CheckHTTPResponse(200).GetRSCloser()
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	resp.SetFirstFakeChunk()
   115  
   116  	// 尝试自主获取文件大小
   117  	if file, ok := ctx.Value(fsctx.FileModelCtx).(model.File); ok {
   118  		resp.SetContentLength(int64(file.Size))
   119  	}
   120  
   121  	return resp, nil
   122  }
   123  
   124  // Put 将文件流保存到指定目录
   125  func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error {
   126  	defer file.Close()
   127  
   128  	return handler.Client.Upload(ctx, file)
   129  }
   130  
   131  // Delete 删除一个或多个文件,
   132  // 返回未删除的文件,及遇到的最后一个错误
   133  func (handler Driver) Delete(ctx context.Context, files []string) ([]string, error) {
   134  	return handler.Client.BatchDelete(ctx, files)
   135  }
   136  
   137  // Thumb 获取文件缩略图
   138  func (handler Driver) Thumb(ctx context.Context, file *model.File) (*response.ContentResponse, error) {
   139  	var (
   140  		thumbSize = [2]uint{400, 300}
   141  		ok        = false
   142  	)
   143  	if thumbSize, ok = ctx.Value(fsctx.ThumbSizeCtx).([2]uint); !ok {
   144  		return nil, errors.New("failed to get thumbnail size")
   145  	}
   146  
   147  	res, err := handler.Client.GetThumbURL(ctx, file.SourceName, thumbSize[0], thumbSize[1])
   148  	if err != nil {
   149  		var apiErr *RespError
   150  		if errors.As(err, &apiErr); err == ErrThumbSizeNotFound || (apiErr != nil && apiErr.APIError.Code == notFoundError) {
   151  			// OneDrive cannot generate thumbnail for this file
   152  			return nil, driver.ErrorThumbNotSupported
   153  		}
   154  	}
   155  
   156  	return &response.ContentResponse{
   157  		Redirect: true,
   158  		URL:      res,
   159  	}, err
   160  }
   161  
   162  // Source 获取外链URL
   163  func (handler Driver) Source(
   164  	ctx context.Context,
   165  	path string,
   166  	ttl int64,
   167  	isDownload bool,
   168  	speed int,
   169  ) (string, error) {
   170  	cacheKey := fmt.Sprintf("onedrive_source_%d_%s", handler.Policy.ID, path)
   171  	if file, ok := ctx.Value(fsctx.FileModelCtx).(model.File); ok {
   172  		cacheKey = fmt.Sprintf("onedrive_source_file_%d_%d", file.UpdatedAt.Unix(), file.ID)
   173  	}
   174  
   175  	// 尝试从缓存中查找
   176  	if cachedURL, ok := cache.Get(cacheKey); ok {
   177  		return handler.replaceSourceHost(cachedURL.(string))
   178  	}
   179  
   180  	// 缓存不存在,重新获取
   181  	res, err := handler.Client.Meta(ctx, "", path)
   182  	if err == nil {
   183  		// 写入新的缓存
   184  		cache.Set(
   185  			cacheKey,
   186  			res.DownloadURL,
   187  			model.GetIntSetting("onedrive_source_timeout", 1800),
   188  		)
   189  		return handler.replaceSourceHost(res.DownloadURL)
   190  	}
   191  	return "", err
   192  }
   193  
   194  func (handler Driver) replaceSourceHost(origin string) (string, error) {
   195  	if handler.Policy.OptionsSerialized.OdProxy != "" {
   196  		source, err := url.Parse(origin)
   197  		if err != nil {
   198  			return "", err
   199  		}
   200  
   201  		cdn, err := url.Parse(handler.Policy.OptionsSerialized.OdProxy)
   202  		if err != nil {
   203  			return "", err
   204  		}
   205  
   206  		// 替换反代地址
   207  		source.Scheme = cdn.Scheme
   208  		source.Host = cdn.Host
   209  		return source.String(), nil
   210  	}
   211  
   212  	return origin, nil
   213  }
   214  
   215  // Token 获取上传会话URL
   216  func (handler Driver) Token(ctx context.Context, ttl int64, uploadSession *serializer.UploadSession, file fsctx.FileHeader) (*serializer.UploadCredential, error) {
   217  	fileInfo := file.Info()
   218  
   219  	uploadURL, err := handler.Client.CreateUploadSession(ctx, fileInfo.SavePath, WithConflictBehavior("fail"))
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	// 监控回调及上传
   225  	go handler.Client.MonitorUpload(uploadURL, uploadSession.Key, fileInfo.SavePath, fileInfo.Size, ttl)
   226  
   227  	uploadSession.UploadURL = uploadURL
   228  	return &serializer.UploadCredential{
   229  		SessionID:  uploadSession.Key,
   230  		ChunkSize:  handler.Policy.OptionsSerialized.ChunkSize,
   231  		UploadURLs: []string{uploadURL},
   232  	}, nil
   233  }
   234  
   235  // 取消上传凭证
   236  func (handler Driver) CancelToken(ctx context.Context, uploadSession *serializer.UploadSession) error {
   237  	return handler.Client.DeleteUploadSession(ctx, uploadSession.UploadURL)
   238  }