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 }