🚧 aliyundrive webdav write

pull/548/head
微凉 2021-12-06 17:49:20 +08:00
parent 1779617cb9
commit 28998d6f8c
11 changed files with 278 additions and 43 deletions

View File

@ -52,7 +52,7 @@ func (driver Cloud189) FormatFile(file *Cloud189File) *model.File {
//func (c Cloud189) GetFile(path string, account *model.Account) (*Cloud189File, error) {
// dir, name := filepath.Split(path)
// dir = utils.ParsePath(dir)
// _, _, err := c.Path(dir, account)
// _, _, err := c.ParentPath(dir, account)
// if err != nil {
// return nil, err
// }

View File

@ -1,6 +1,7 @@
package alidrive
import (
"errors"
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
@ -9,6 +10,7 @@ import (
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"path/filepath"
"strings"
"time"
)
@ -156,6 +158,81 @@ func (driver AliDrive) RefreshToken(account *model.Account) error {
return nil
}
func (driver AliDrive) Rename(fileId, name string, account *model.Account) error {
var resp base.Json
var e AliRespError
_, err := aliClient.R().SetResult(&resp).SetError(&e).
SetHeader("authorization", "Bearer\t"+account.AccessToken).
SetBody(base.Json{
"check_name_mode": "refuse",
"drive_id": account.DriveId,
"file_id": fileId,
"name": name,
}).Post("https://api.aliyundrive.com/v3/file/update")
if err != nil {
return err
}
if e.Code != "" {
if e.Code == "AccessTokenInvalid" {
err = driver.RefreshToken(account)
if err != nil {
return err
} else {
_ = model.SaveAccount(account)
return driver.Rename(fileId, name, account)
}
}
return fmt.Errorf("%s", e.Message)
}
if resp["name"] == name {
return nil
}
return fmt.Errorf("%+v", resp)
}
func (driver AliDrive) Batch(srcId,dstId string, account *model.Account) error {
var e AliRespError
res, err := aliClient.R().SetError(&e).
SetHeader("authorization", "Bearer\t"+account.AccessToken).
SetBody(base.Json{
"requests": []base.Json{
{
"headers": base.Json{
"Content-Type": "application/json",
},
"method":"POST",
"id":srcId,
"body":base.Json{
"drive_id": account.DriveId,
"file_id":srcId,
"to_drive_id":account.DriveId,
"to_parent_file_id":dstId,
},
},
},
"resource": "file",
}).Post("https://api.aliyundrive.com/v3/batch")
if err != nil {
return err
}
if e.Code != "" {
if e.Code == "AccessTokenInvalid" {
err = driver.RefreshToken(account)
if err != nil {
return err
} else {
_ = model.SaveAccount(account)
return driver.Batch(srcId, dstId, account)
}
}
return fmt.Errorf("%s", e.Message)
}
if strings.Contains(res.String(), `"status":200`) {
return nil
}
return errors.New(res.String())
}
func init() {
base.RegisterDriver(&AliDrive{})
aliClient.

View File

@ -9,6 +9,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/robfig/cron/v3"
log "github.com/sirupsen/logrus"
"math"
"path/filepath"
)
@ -16,7 +17,7 @@ type AliDrive struct{}
func (driver AliDrive) Config() base.DriverConfig {
return base.DriverConfig{
Name: "AliDrive",
Name: "AliDrive",
OnlyProxy: false,
}
}
@ -130,7 +131,7 @@ func (driver AliDrive) File(path string, account *model.Account) (*model.File, e
func (driver AliDrive) Files(path string, account *model.Account) ([]model.File, error) {
path = utils.ParsePath(path)
var rawFiles []AliFile
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
cache, err := base.GetCache(path, account)
if err == nil {
rawFiles, _ = cache.([]AliFile)
} else {
@ -143,7 +144,7 @@ func (driver AliDrive) Files(path string, account *model.Account) ([]model.File,
return nil, err
}
if len(rawFiles) > 0 {
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
_ = base.SetCache(path, rawFiles, account)
}
}
files := make([]model.File, 0)
@ -249,23 +250,152 @@ func (driver AliDrive) Preview(path string, account *model.Account) (interface{}
}
func (driver AliDrive) MakeDir(path string, account *model.Account) error {
return base.ErrNotImplement
dir, name := filepath.Split(path)
parentFile, err := driver.File(dir, account)
if err != nil {
return err
}
if !parentFile.IsDir() {
return base.ErrNotFolder
}
var resp base.Json
var e AliRespError
_, err = aliClient.R().SetResult(&resp).SetError(&e).
SetHeader("authorization", "Bearer\t"+account.AccessToken).
SetBody(base.Json{
"check_name_mode": "refuse",
"drive_id": account.DriveId,
"name": name,
"parent_file_id": parentFile.Id,
"type": "folder",
}).Post("https://api.aliyundrive.com/adrive/v2/file/createWithFolders")
if e.Code != "" {
if e.Code == "AccessTokenInvalid" {
err = driver.RefreshToken(account)
if err != nil {
return err
} else {
_ = model.SaveAccount(account)
return driver.MakeDir(path, account)
}
}
return fmt.Errorf("%s", e.Message)
}
if resp["file_name"] == name {
_ = base.DeleteCache(dir, account)
return nil
}
return fmt.Errorf("%+v", resp)
}
func (driver AliDrive) Move(src string, dst string, account *model.Account) error {
return base.ErrNotImplement
srcDir, _ := filepath.Split(src)
dstDir, dstName := filepath.Split(dst)
srcFile, err := driver.File(src, account)
if err != nil {
return err
}
// rename
if srcDir == dstDir {
err = driver.Rename(srcFile.Id, dstName, account)
} else {
// move
dstDirFile, err := driver.File(dstDir, account)
if err != nil {
return err
}
err = driver.Batch(srcFile.Id, dstDirFile.Id, account)
}
if err != nil {
_ = base.DeleteCache(srcDir, account)
_ = base.DeleteCache(dstDir, account)
}
return err
}
func (driver AliDrive) Copy(src string, dst string, account *model.Account) error {
return base.ErrNotImplement
return base.ErrNotSupport
}
func (driver AliDrive) Delete(path string, account *model.Account) error {
return base.ErrNotImplement
file, err := driver.File(path, account)
if err != nil {
return err
}
var resp base.Json
var e AliRespError
_, err = aliClient.R().SetResult(&resp).SetError(&e).
SetHeader("authorization", "Bearer\t"+account.AccessToken).
SetBody(base.Json{
"drive_id": account.DriveId,
"file_id": file.Id,
}).Post("https://api.aliyundrive.com/v2/recyclebin/trash")
if e.Code != "" {
if e.Code == "AccessTokenInvalid" {
err = driver.RefreshToken(account)
if err != nil {
return err
} else {
_ = model.SaveAccount(account)
return driver.Delete(path, account)
}
}
return fmt.Errorf("%s", e.Message)
}
if resp["file_id"] == file.Id {
_ = base.DeleteCache(utils.Dir(path), account)
return nil
}
return fmt.Errorf("%+v", resp)
}
type UploadResp struct {
}
func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) error {
const DEFAULT int64 = 10485760
var count = math.Ceil(float64(file.GetSize()) / float64(DEFAULT))
//var finish int64 = 0
parentFile, err := driver.File(file.ParentPath, account)
if err != nil {
return err
}
var resp UploadResp
var e AliRespError
partInfoList := make([]base.Json, 0)
for i := 0; i < int(count); i++ {
partInfoList = append(partInfoList, base.Json{
"part_number": i + 1,
})
}
_, err = aliClient.R().SetResult(&resp).SetError(&e).
SetHeader("authorization", "Bearer\t"+account.AccessToken).
SetBody(base.Json{
"check_name_mode": "auto_rename",
// content_hash
"content_hash_name": "none",
"drive_id": account.DriveId,
"name": file.GetFileName(),
"parent_file_id": parentFile.Id,
"part_info_list": partInfoList,
//proof_code
"proof_version": "v1",
"size": file.GetSize(),
"type": "file",
}).Post("https://api.aliyundrive.com/v2/recyclebin/trash")
if e.Code != "" {
if e.Code == "AccessTokenInvalid" {
err = driver.RefreshToken(account)
if err != nil {
return err
} else {
_ = model.SaveAccount(account)
return driver.Upload(file, account)
}
}
return fmt.Errorf("%s", e.Message)
}
return base.ErrNotImplement
}
var _ base.Driver = (*AliDrive)(nil)
var _ base.Driver = (*AliDrive)(nil)

25
drivers/base/cache.go Normal file
View File

@ -0,0 +1,25 @@
package base
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
)
func KeyCache(path string, account *model.Account) string {
path = utils.ParsePath(path)
return fmt.Sprintf("%s%s", account.Name, path)
}
func SetCache(path string, obj interface{}, account *model.Account) error {
return conf.Cache.Set(conf.Ctx, KeyCache(path, account), obj, nil)
}
func GetCache(path string, account *model.Account) (interface{}, error) {
return conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", KeyCache(path, account)))
}
func DeleteCache(path string, account *model.Account) error {
return conf.Cache.Delete(conf.Ctx, fmt.Sprintf("%s%s", KeyCache(path, account)))
}

View File

@ -41,11 +41,6 @@ type Item struct {
Description string `json:"description"`
}
type TokenResp struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
var driversMap = map[string]Driver{}
func RegisterDriver(driver Driver) {
@ -68,14 +63,14 @@ func GetDrivers() map[string][]Item {
{
Name: "proxy",
Label: "proxy",
Type: "bool",
Type: TypeBool,
Required: true,
Description: "allow proxy",
},
{
Name: "webdav_proxy",
Label: "webdav proxy",
Type: "bool",
Type: TypeBool,
Required: true,
Description: "Transfer the WebDAV of this account through the server",
},
@ -85,8 +80,6 @@ func GetDrivers() map[string][]Item {
return res
}
type Json map[string]interface{}
var NoRedirectClient *resty.Client
func init() {

View File

@ -1,12 +1,15 @@
package base
import "fmt"
import (
"errors"
)
var (
ErrPathNotFound = fmt.Errorf("path not found")
ErrNotFile = fmt.Errorf("not file")
ErrNotImplement = fmt.Errorf("not implement")
ErrNotSupport = fmt.Errorf("not support")
ErrPathNotFound = errors.New("path not found")
ErrNotFile = errors.New("not file")
ErrNotImplement = errors.New("not implement")
ErrNotSupport = errors.New("not support")
ErrNotFolder = errors.New("not a folder")
)
const (
@ -15,3 +18,10 @@ const (
TypeBool = "bool"
TypeNumber = "number"
)
type Json map[string]interface{}
type TokenResp struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}

View File

@ -134,7 +134,7 @@ func (driver GoogleDrive) GetFiles(id string, account *model.Account) ([]GoogleF
//func (driver GoogleDrive) GetFile(path string, account *model.Account) (*GoogleFile, error) {
// dir, name := filepath.Split(path)
// dir = utils.ParsePath(dir)
// _, _, err := driver.Path(dir, account)
// _, _, err := driver.ParentPath(dir, account)
// if err != nil {
// return nil, err
// }

View File

@ -204,8 +204,8 @@ func (driver Native) Delete(path string, account *model.Account) error {
}
func (driver Native) Upload(file *model.FileStream, account *model.Account) error {
fullPath := filepath.Join(account.RootFolder, file.Path, file.Name)
_, err := driver.File(filepath.Join(file.Path,file.Name), account)
fullPath := filepath.Join(account.RootFolder, file.ParentPath, file.Name)
_, err := driver.File(filepath.Join(file.ParentPath,file.Name), account)
if err == nil {
// TODO overwrite?
}

View File

@ -3,11 +3,11 @@ package model
import "io"
type FileStream struct {
File io.ReadCloser
Size uint64
Path string
Name string
MIMEType string
File io.ReadCloser
Size uint64
ParentPath string
Name string
MIMEType string
}
func (file FileStream) Read(p []byte) (n int, err error) {
@ -30,6 +30,6 @@ func (file FileStream) GetFileName() string {
return file.Name
}
func (file FileStream) GetPath() string {
return file.Path
func (file FileStream) GetParentPath() string {
return file.ParentPath
}

View File

@ -11,7 +11,7 @@ import (
)
type PathReq struct {
Path string `json:"Path"`
Path string `json:"ParentPath"`
Password string `json:"Password"`
}

View File

@ -23,16 +23,16 @@ import (
type FileSystem struct{}
func ParsePath(rawPath string) (*model.Account, string, base.Driver, error) {
var path, name string
var internalPath, name string
switch model.AccountsCount() {
case 0:
return nil, "", nil, fmt.Errorf("no accounts,please add one first")
case 1:
path = rawPath
internalPath = rawPath
break
default:
paths := strings.Split(rawPath, "/")
path = "/" + strings.Join(paths[2:], "/")
internalPath = "/" + strings.Join(paths[2:], "/")
name = paths[1]
}
account, ok := model.GetAccount(name)
@ -43,7 +43,7 @@ func ParsePath(rawPath string) (*model.Account, string, base.Driver, error) {
if !ok {
return nil, "", nil, fmt.Errorf("no [%s] driver", account.Type)
}
return &account, path, driver, nil
return &account, internalPath, driver, nil
}
func (fs *FileSystem) File(rawPath string) (*model.File, error) {
@ -160,11 +160,11 @@ func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath strin
}
filePath, fileName := filepath.Split(path_)
fileData := model.FileStream{
MIMEType: r.Header.Get("Content-Type"),
File: r.Body,
Size: fileSize,
Name: fileName,
Path: filePath,
MIMEType: r.Header.Get("Content-Type"),
File: r.Body,
Size: fileSize,
Name: fileName,
ParentPath: filePath,
}
return driver.Upload(&fileData, account)
}