From eecea3febd890294904d63f27fdbd977b77a8fa2 Mon Sep 17 00:00:00 2001 From: potoo <34411681+potoo0@users.noreply.github.com> Date: Thu, 2 May 2024 22:27:31 +0800 Subject: [PATCH] fix(onedrive): fix Ctime/Mtime (#6397) --- drivers/onedrive/driver.go | 1 + drivers/onedrive/types.go | 31 +++++++++++++++++++------ drivers/onedrive/util.go | 47 +++++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/drivers/onedrive/driver.go b/drivers/onedrive/driver.go index 319fd906..adbe0342 100644 --- a/drivers/onedrive/driver.go +++ b/drivers/onedrive/driver.go @@ -118,6 +118,7 @@ func (d *Onedrive) MakeDir(ctx context.Context, parentDir model.Obj, dirName str "folder": base.Json{}, "@microsoft.graph.conflictBehavior": "rename", } + // todo 修复文件夹 ctime/mtime, onedrive 可在 data 里设置 fileSystemInfo 字段, 但是此接口未提供 ctime/mtime _, err := d.Request(url, http.MethodPost, func(req *resty.Request) { req.SetBody(data) }, nil) diff --git a/drivers/onedrive/types.go b/drivers/onedrive/types.go index 69264abc..aedd5a06 100644 --- a/drivers/onedrive/types.go +++ b/drivers/onedrive/types.go @@ -24,12 +24,12 @@ type RespErr struct { } type File struct { - Id string `json:"id"` - Name string `json:"name"` - Size int64 `json:"size"` - LastModifiedDateTime time.Time `json:"lastModifiedDateTime"` - Url string `json:"@microsoft.graph.downloadUrl"` - File *struct { + Id string `json:"id"` + Name string `json:"name"` + Size int64 `json:"size"` + FileSystemInfo *FileSystemInfoFacet `json:"fileSystemInfo"` + Url string `json:"@microsoft.graph.downloadUrl"` + File *struct { MimeType string `json:"mimeType"` } `json:"file"` Thumbnails []struct { @@ -58,7 +58,7 @@ func fileToObj(f File, parentID string) *Object { ID: f.Id, Name: f.Name, Size: f.Size, - Modified: f.LastModifiedDateTime, + Modified: f.FileSystemInfo.LastModifiedDateTime, IsFolder: f.File == nil, }, Thumbnail: model.Thumbnail{Thumbnail: thumb}, @@ -72,3 +72,20 @@ type Files struct { Value []File `json:"value"` NextLink string `json:"@odata.nextLink"` } + +// Metadata represents a request to update Metadata. +// It includes only the writeable properties. +// omitempty is intentionally included for all, per https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_update?view=odsp-graph-online#request-body +type Metadata struct { + Description string `json:"description,omitempty"` // Provides a user-visible description of the item. Read-write. Only on OneDrive Personal. Undocumented limit of 1024 characters. + FileSystemInfo *FileSystemInfoFacet `json:"fileSystemInfo,omitempty"` // File system information on client. Read-write. +} + +// FileSystemInfoFacet contains properties that are reported by the +// device's local file system for the local version of an item. This +// facet can be used to specify the last modified date or created date +// of the item as it was on the local device. +type FileSystemInfoFacet struct { + CreatedDateTime time.Time `json:"createdDateTime,omitempty"` // The UTC date and time the file was created on a client. + LastModifiedDateTime time.Time `json:"lastModifiedDateTime,omitempty"` // The UTC date and time the file was last modified on a client. +} diff --git a/drivers/onedrive/util.go b/drivers/onedrive/util.go index a0c6fa8f..9ee2dae9 100644 --- a/drivers/onedrive/util.go +++ b/drivers/onedrive/util.go @@ -127,7 +127,7 @@ func (d *Onedrive) Request(url string, method string, callback base.ReqCallback, func (d *Onedrive) getFiles(path string) ([]File, error) { var res []File - nextLink := d.GetMetaUrl(false, path) + "/children?$top=5000&$expand=thumbnails($select=medium)&$select=id,name,size,lastModifiedDateTime,content.downloadUrl,file,parentReference" + nextLink := d.GetMetaUrl(false, path) + "/children?$top=5000&$expand=thumbnails($select=medium)&$select=id,name,size,fileSystemInfo,content.downloadUrl,file,parentReference" for nextLink != "" { var files Files _, err := d.Request(nextLink, http.MethodGet, nil, &files) @@ -148,7 +148,10 @@ func (d *Onedrive) GetFile(path string) (*File, error) { } func (d *Onedrive) upSmall(ctx context.Context, dstDir model.Obj, stream model.FileStreamer) error { - url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetPath(), stream.GetName())) + "/content" + filepath := stdpath.Join(dstDir.GetPath(), stream.GetName()) + // 1. upload new file + // ApiDoc: https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_put_content?view=odsp-graph-online + url := d.GetMetaUrl(false, filepath) + "/content" data, err := io.ReadAll(stream) if err != nil { return err @@ -156,12 +159,50 @@ func (d *Onedrive) upSmall(ctx context.Context, dstDir model.Obj, stream model.F _, err = d.Request(url, http.MethodPut, func(req *resty.Request) { req.SetBody(data).SetContext(ctx) }, nil) + if err != nil { + return fmt.Errorf("onedrive: Failed to upload new file(path=%v): %w", filepath, err) + } + + // 2. update metadata + err = d.updateMetadata(ctx, stream, filepath) + if err != nil { + return fmt.Errorf("onedrive: Failed to update file(path=%v) metadata: %w", filepath, err) + } + return nil +} + +func (d *Onedrive) updateMetadata(ctx context.Context, stream model.FileStreamer, filepath string) error { + url := d.GetMetaUrl(false, filepath) + metadata := toAPIMetadata(stream) + // ApiDoc: https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_update?view=odsp-graph-online + _, err := d.Request(url, http.MethodPatch, func(req *resty.Request) { + req.SetBody(metadata).SetContext(ctx) + }, nil) return err } +func toAPIMetadata(stream model.FileStreamer) Metadata { + metadata := Metadata{ + FileSystemInfo: &FileSystemInfoFacet{}, + } + if !stream.ModTime().IsZero() { + metadata.FileSystemInfo.LastModifiedDateTime = stream.ModTime() + } + if !stream.CreateTime().IsZero() { + metadata.FileSystemInfo.CreatedDateTime = stream.CreateTime() + } + if stream.CreateTime().IsZero() && !stream.ModTime().IsZero() { + metadata.FileSystemInfo.CreatedDateTime = stream.CreateTime() + } + return metadata +} + func (d *Onedrive) upBig(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetPath(), stream.GetName())) + "/createUploadSession" - res, err := d.Request(url, http.MethodPost, nil, nil) + metadata := map[string]interface{}{"item": toAPIMetadata(stream)} + res, err := d.Request(url, http.MethodPost, func(req *resty.Request) { + req.SetBody(metadata).SetContext(ctx) + }, nil) if err != nil { return err }