diff --git a/drivers/onedrive/driver.go b/drivers/onedrive/driver.go index 0c8c90f3..fb450032 100644 --- a/drivers/onedrive/driver.go +++ b/drivers/onedrive/driver.go @@ -210,23 +210,85 @@ func (driver Onedrive) Preview(path string, account *model.Account) (interface{} } func (driver Onedrive) MakeDir(path string, account *model.Account) error { - return base.ErrNotImplement + url := driver.GetMetaUrl(account, false, utils.Dir(path)) + "/children" + data := base.Json{ + "name": utils.Base(path), + "folder": base.Json{}, + "@microsoft.graph.conflictBehavior": "rename", + } + _, err := driver.Request(url, base.Post, nil, nil, nil, &data, nil, account) + if err == nil { + _ = base.DeleteCache(utils.Dir(path), account) + } + return err } func (driver Onedrive) Move(src string, dst string, account *model.Account) error { - return base.ErrNotImplement + log.Debugf("onedrive move") + dstParentFile, err := driver.GetFile(account, utils.Dir(dst)) + if err != nil { + return err + } + data := base.Json{ + "parentReference": base.Json{ + "id": dstParentFile.Id, + }, + "name": utils.Base(dst), + } + url := driver.GetMetaUrl(account, false, src) + _, err = driver.Request(url, base.Patch, nil, nil, nil, &data, nil, account) + if err == nil { + _ = base.DeleteCache(utils.Dir(src), account) + if utils.Dir(src) != utils.Dir(dst) { + _ = base.DeleteCache(utils.Dir(dst), account) + } + } + return err } func (driver Onedrive) Copy(src string, dst string, account *model.Account) error { - return base.ErrNotImplement + dstParentFile, err := driver.GetFile(account, utils.Dir(dst)) + if err != nil { + return err + } + data := base.Json{ + "parentReference": base.Json{ + "driveId": dstParentFile.ParentReference.DriveId, + "id": dstParentFile.Id, + }, + "name": utils.Base(dst), + } + url := driver.GetMetaUrl(account, false, src) + "/copy" + _, err = driver.Request(url, base.Post, nil, nil, nil, &data, nil, account) + if err == nil { + _ = base.DeleteCache(utils.Dir(src), account) + if utils.Dir(src) != utils.Dir(dst) { + _ = base.DeleteCache(utils.Dir(dst), account) + } + } + return err } func (driver Onedrive) Delete(path string, account *model.Account) error { - return base.ErrNotImplement + url := driver.GetMetaUrl(account, false, path) + _, err := driver.Request(url, base.Delete, nil, nil, nil, nil, nil, account) + if err == nil { + _ = base.DeleteCache(utils.Dir(path), account) + } + return err } func (driver Onedrive) Upload(file *model.FileStream, account *model.Account) error { - return base.ErrNotImplement + var err error + if file.GetSize() <= 4*1024*1024 { + err = driver.UploadSmall(file, account) + } else { + err = driver.UploadBig(file, account) + } + if err == nil { + _ = base.DeleteCache(utils.Dir(file.ParentPath), account) + } + return err } var _ base.Driver = (*Onedrive)(nil) diff --git a/drivers/onedrive/onedrive.go b/drivers/onedrive/onedrive.go index 5c720def..9967b950 100644 --- a/drivers/onedrive/onedrive.go +++ b/drivers/onedrive/onedrive.go @@ -1,6 +1,7 @@ package onedrive import ( + "bytes" "errors" "fmt" "github.com/Xhofe/alist/conf" @@ -8,8 +9,13 @@ import ( "github.com/Xhofe/alist/model" "github.com/Xhofe/alist/utils" "github.com/go-resty/resty/v2" + jsoniter "github.com/json-iterator/go" log "github.com/sirupsen/logrus" + "io" + "io/ioutil" + "net/http" "path/filepath" + "strconv" "time" ) @@ -111,6 +117,7 @@ func (driver Onedrive) refreshToken(account *model.Account) error { } type OneFile struct { + Id string `json:"id"` Name string `json:"name"` Size int64 `json:"size"` LastModifiedDateTime *time.Time `json:"lastModifiedDateTime"` @@ -118,11 +125,14 @@ type OneFile struct { File struct { MimeType string `json:"mimeType"` } `json:"file"` - Thumbnails []struct{ - Medium struct{ + Thumbnails []struct { + Medium struct { Url string `json:"url"` } `json:"medium"` } `json:"thumbnails"` + ParentReference struct { + DriveId string `json:"driveId"` + } `json:"parentReference"` } type OneFiles struct { @@ -144,6 +154,7 @@ func (driver Onedrive) FormatFile(file *OneFile) *model.File { UpdatedAt: file.LastModifiedDateTime, Driver: driver.Config().Name, Url: file.Url, + Id: file.Id, } if len(file.Thumbnails) > 0 { f.Thumbnail = file.Thumbnails[0].Medium.Url @@ -198,7 +209,110 @@ func (driver Onedrive) GetFile(account *model.Account, path string) (*OneFile, e return &file, nil } +func (driver Onedrive) Request(url string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) { + rawUrl := url + if account.APIProxyUrl != "" { + url = fmt.Sprintf("%s/%s", account.APIProxyUrl, url) + } + req := base.RestyClient.R() + req.SetHeader("Authorization", "Bearer "+account.AccessToken) + if headers != nil { + req.SetHeaders(headers) + } + if query != nil { + req.SetQueryParams(query) + } + if form != nil { + req.SetFormData(form) + } + if data != nil { + req.SetBody(data) + } + if resp != nil { + req.SetResult(resp) + } + var res *resty.Response + var err error + var e OneRespErr + req.SetError(&e) + switch method { + case base.Get: + res, err = req.Get(url) + case base.Post: + res, err = req.Post(url) + case base.Patch: + res, err = req.Patch(url) + case base.Delete: + res, err = req.Delete(url) + case base.Put: + res, err = req.Put(url) + default: + return nil, base.ErrNotSupport + } + if err != nil { + return nil, err + } + log.Debug(res.String()) + if e.Error.Code != "" { + if e.Error.Code == "InvalidAuthenticationToken" { + err = driver.RefreshToken(account) + if err != nil { + _ = model.SaveAccount(account) + return nil, err + } + return driver.Request(rawUrl, method, headers, query, form, data, resp, account) + } + return nil, errors.New(e.Error.Message) + } + return res.Body(), nil +} + +func (driver Onedrive) UploadSmall(file *model.FileStream, account *model.Account) error { + url := driver.GetMetaUrl(account, false, utils.Join(file.ParentPath, file.Name)) + "/content" + data, err := ioutil.ReadAll(file) + if err != nil { + return err + } + _, err = driver.Request(url, base.Put, nil, nil, nil, data, nil, account) + return err +} + +func (driver Onedrive) UploadBig(file *model.FileStream, account *model.Account) error { + url := driver.GetMetaUrl(account, false, utils.Join(file.ParentPath, file.Name)) + "/createUploadSession" + res, err := driver.Request(url, base.Post, nil, nil, nil, nil, nil, account) + if err != nil { + return err + } + uploadUrl := jsoniter.Get(res, "uploadUrl").ToString() + var finish uint64 = 0 + const DEFAULT = 4 * 1024 * 1024 + for finish < file.GetSize() { + log.Debugf("upload: %d", finish) + var byteSize uint64 = DEFAULT + left := file.GetSize() - finish + if left < DEFAULT { + byteSize = left + } + byteData := make([]byte, byteSize) + n, err := io.ReadFull(file, byteData) + log.Debug(err, n) + if err != nil { + return err + } + req, err := http.NewRequest("PUT", uploadUrl, bytes.NewBuffer(byteData)) + req.Header.Set("Content-Length", strconv.Itoa(int(byteSize))) + req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", finish, finish+byteSize-1, file.Size)) + finish += byteSize + res, err := base.HttpClient.Do(req) + if res.StatusCode != 201 && res.StatusCode != 202 { + data, _ := ioutil.ReadAll(res.Body) + return errors.New(string(data)) + } + } + return nil +} + func init() { base.RegisterDriver(&Onedrive{}) oneClient.SetRetryCount(3) -} \ No newline at end of file +} diff --git a/utils/file.go b/utils/file.go index 354dc496..0608b983 100644 --- a/utils/file.go +++ b/utils/file.go @@ -6,6 +6,7 @@ import ( log "github.com/sirupsen/logrus" "io/ioutil" "os" + "path" "path/filepath" "strings" ) @@ -34,7 +35,7 @@ func GetFileType(ext string) int { if ext == "" { return conf.UNKNOWN } - ext = strings.ToLower(strings.TrimLeft(ext,".")) + ext = strings.ToLower(strings.TrimLeft(ext, ".")) if IsContain(conf.OfficeTypes, ext) { return conf.OFFICE } @@ -116,9 +117,9 @@ func Base(path string) string { } func Join(elem ...string) string { - res := filepath.Join(elem...) + res := path.Join(elem...) if res == "\\" { res = "/" } return res -} \ No newline at end of file +}