Repository: AlistGo/alist (language: go)

Issue:
ct189 - uploaded part number less than total slice part number, please continue uploaded!
### Alist Version / Alist 版本

dev branch

### Describe the bug / 问题描述
通过NAS 同步几万个文件，有20多个文件无法同步，错误如下

![image](https://user-images.githubusercontent.com/32152540/153759605-ca579062-a89c-49cf-9294-7895349e35f4.png)

提供2个示例文件，后缀名随便改的 要不发不上来

https://user-images.githubusercontent.com/32152540/153759942-93d4c505-f4a6-4d59-bd93-0702c0c320aa.mp4



https://user-images.githubusercontent.com/32152540/153759842-fe82ee13-cf66-4b27-85a4-5ea8ff4e1436.mp4



### Reproduction / 复现链接

_No response_

### 日志 / Logs

_No response_
OpenWrt 等使用 tmpfs 的嵌入式设备上传大文件时会导致 /tmp 空间爆满
### Alist Version / Alist 版本

所有

### Describe the bug / 问题描述

无法自定义临时目录位置，OpenWrt 等使用 tmpfs 的嵌入式设备上传大文件时会导致 /tmp 空间爆满。

### Reproduction / 复现链接

_No response_

### 日志 / Logs

_No response_


Discussion / hints:

Fix patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index ef8eef2d1cf..4230ab26cb2 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -5,7 +5,7 @@ body:
   - type: markdown
     attributes:
       value: |
-        Thanks for taking the time to fill out this bug report!
+        Thanks for taking the time to fill out this bug report, please confirm that your issue is not a duplicate issue
   - type: input
     id: version
     attributes:
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index faa8be162ab..be284ab6178 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,5 @@
 blank_issues_enabled: false
 contact_links:
-  - name: Questions & Discussions & Feature request
+  - name: Questions & Discussions
     url: https://github.com/Xhofe/alist/discussions
-    about: Use GitHub discussions for message-board style questions and discussions or feature request.
\ No newline at end of file
+    about: Use GitHub discussions for message-board style questions and discussions.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 00000000000..b59c42f57c5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,24 @@
+name: "Feature request"
+description: Feature request
+labels: ["enhancement: pending triage"]
+body:
+  - type: textarea
+    id: feature-description
+    attributes:
+      label: Description of the feature / 需求描述
+    validations:
+      required: true
+  - type: textarea
+    id: suggested-solution
+    attributes:
+      label: Suggested solution / 实现思路
+      description: |
+        Solutions to achieve this requirement.
+        实现此需求的解决思路。
+  - type: textarea
+    id: additional-context
+    attributes:
+      label: Additional context / 附件
+      description: |
+        Any other context or screenshots about the feature request here, or information you find helpful.
+        相关的任何其他上下文或截图，或者你觉得有帮助的信息
\ No newline at end of file
diff --git a/.github/workflows/issue_check_question.yml b/.github/workflows/issue_check_question.yml
new file mode 100644
index 00000000000..3c281fe99a4
--- /dev/null
+++ b/.github/workflows/issue_check_question.yml
@@ -0,0 +1,20 @@
+name: Check need info
+
+on:
+  schedule:
+    - cron: "0 0 */7 * *"
+
+jobs:
+  check-need-info:
+    runs-on: ubuntu-latest
+    steps:
+      - name: close-issues
+        uses: actions-cool/issues-helper@v2
+        with:
+          actions: 'close-issues'
+          token: ${{ secrets.GITHUB_TOKEN }}
+          labels: 'question'
+          inactive-day: 7
+          body: |
+            Hello @${{ github.event.issue.user.login }}, this issue was closed due to no activities in 7 days.
+            你好 @${{ github.event.issue.user.login }}，此issue因超过7天未回复被关闭。
\ No newline at end of file
diff --git a/.github/workflows/issue_duplicate.yml b/.github/workflows/issue_duplicate.yml
new file mode 100644
index 00000000000..e837020410c
--- /dev/null
+++ b/.github/workflows/issue_duplicate.yml
@@ -0,0 +1,25 @@
+name: Issue Duplicate
+
+on:
+  issues:
+    types: [labeled]
+
+jobs:
+  create-comment:
+    runs-on: ubuntu-latest
+    if: github.event.label.name == 'duplicate'
+    steps:
+      - name: Create comment
+        uses: actions-cool/issues-helper@v2
+        with:
+          actions: 'create-comment'
+          token: ${{ secrets.GITHUB_TOKEN }}
+          issue-number: ${{ github.event.issue.number }}
+          body: |
+            Hello @${{ github.event.issue.user.login }}, your issue is a duplicate and will be closed.
+            你好 @${{ github.event.issue.user.login }}，你的issue是重复的，将被关闭。
+      - name: Close issue
+        uses: actions-cool/issues-helper@v2
+        with:
+          actions: 'close-issue'
+          token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/issue_month_statistics.yml b/.github/workflows/issue_month_statistics.yml
new file mode 100644
index 00000000000..c4659b615f4
--- /dev/null
+++ b/.github/workflows/issue_month_statistics.yml
@@ -0,0 +1,17 @@
+name: Issue Month Statistics
+
+on:
+  schedule:
+    - cron: "0 1 1 * *"
+
+jobs:
+  month-statistics:
+    runs-on: ubuntu-latest
+    steps:
+      - name: month-statistics
+        uses: actions-cool/issues-helper@v2
+        with:
+          actions: 'month-statistics'
+          token: ${{ secrets.GITHUB_TOKEN }}
+          count-lables: 'true'
+          count-comments: 'true'
\ No newline at end of file
diff --git a/.github/workflows/issue_question.yml b/.github/workflows/issue_question.yml
new file mode 100644
index 00000000000..d46286fe3de
--- /dev/null
+++ b/.github/workflows/issue_question.yml
@@ -0,0 +1,20 @@
+name: Issue Question
+
+on:
+  issues:
+    types: [labeled]
+
+jobs:
+  create-comment:
+    runs-on: ubuntu-latest
+    if: github.event.label.name == 'question'
+    steps:
+      - name: Create comment
+        uses: actions-cool/issues-helper@v2.0.0
+        with:
+          actions: 'create-comment'
+          token: ${{ secrets.GITHUB_TOKEN }}
+          issue-number: ${{ github.event.issue.number }}
+          body: |
+            Hello @${{ github.event.issue.user.login }}, please input issue by template and add detail. Issues labeled by `question` will be closed if no activities in 7 days.
+            你好 @${{ github.event.issue.user.login }}，请按照issue模板填写, 并详细说明问题/复现步骤/实现思路或提供更多信息等, 7天内未回复issue自动关闭。
\ No newline at end of file
diff --git a/.github/workflows/issue_wontfix.yml b/.github/workflows/issue_wontfix.yml
new file mode 100644
index 00000000000..142ab156206
--- /dev/null
+++ b/.github/workflows/issue_wontfix.yml
@@ -0,0 +1,25 @@
+name: Issue Wontfix
+
+on:
+  issues:
+    types: [labeled]
+
+jobs:
+  lock-issue:
+    runs-on: ubuntu-latest
+    if: github.event.label.name == 'wontfix'
+    steps:
+      - name: Create comment
+        uses: actions-cool/issues-helper@v2
+        with:
+          actions: 'create-comment'
+          token: ${{ secrets.GITHUB_TOKEN }}
+          issue-number: ${{ github.event.issue.number }}
+          body: |
+            Hello @${{ github.event.issue.user.login }}, this issue will not be worked on and will be closed.
+            你好 @${{ github.event.issue.user.login }}，这不会被处理，将被关闭。
+      - name: Close issue
+        uses: actions-cool/issues-helper@v2
+        with:
+          actions: 'close-issue'
+          token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/README.md b/README.md
index 4f9f2e6b1b4..cc13f9c5d31 100755
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 <div align="center">
-  <h3><a href="https://alist.nn.ci">Alist</a></h3>
+  <a href="https://alist.nn.ci"><img height="100px" alt="logo" src="https://cdn.jsdelivr.net/gh/alist-org/assets@main/logo.svg"/></a>
   <p><em>🗂️Another file list program that supports multiple storage, powered by Gin and React.</em></p>
   <a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
   <a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
@@ -12,6 +12,7 @@
 </div>
 
 
+
 ---
 
 English | [中文](./README_cn.md)
@@ -35,6 +36,8 @@ English | [中文](./README_cn.md)
   - [x] Teambition([China](https://www.teambition.com/ ),[International](https://us.teambition.com/ ))
   - [x] [Mediatrack](https://www.mediatrack.cn/)
   - [x] [139yun](https://yun.139.com/) (Personal, Family)
+  - [x] [Yandex.Disk](https://disk.yandex.com/)
+  - [x] [Baidu Disk](http://pan.baidu.com/)
 - [x] Easy to deploy and out-of-the-box
 - [x] File preview (PDF, markdown, code, plain text, ...)
 - [x] Image preview in gallery mode
@@ -50,11 +53,11 @@ English | [中文](./README_cn.md)
 - [x] Cloudflare workers proxy
 - [x] File/Folder package download
 - [x] Support video list playback and subtitles(ass,srt,vtt)
-- [x] Web upload(Can allow visitors to upload), delete, mkdir, rename and move
+- [x] Web upload(Can allow visitors to upload), delete, mkdir, rename, move and copy
 
 ## Discussion
 
-Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports  only.**
+Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports and feature request only.**
 
 ## Demo
 
diff --git a/README_cn.md b/README_cn.md
index eb4db3edc90..0b4eeaf6b94 100644
--- a/README_cn.md
+++ b/README_cn.md
@@ -1,5 +1,5 @@
 <div align="center">
-  <h3><a href="https://alist.nn.ci">Alist</a></h3>
+  <a href="https://alist.nn.ci"><img height="100px" alt="logo" src="https://cdn.jsdelivr.net/gh/alist-org/assets@main/logo.svg"/></a>
   <p><em>🗂️一个支持多存储的文件列表程序，使用 Gin 和 React 。</em></p>
   <a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
   <a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
@@ -11,6 +11,7 @@
   </a>
 </div>
 
+
 ---
 
 [English](./README.md) | 中文
@@ -34,6 +35,8 @@
   - [x] Teambition（[中国](https://www.teambition.com/ )，[国际](https://us.teambition.com/ )）
   - [x] [分秒帧](https://www.mediatrack.cn/)
   - [x] [和彩云](https://yun.139.com/) (个人云, 家庭云)
+  - [x] [Yandex.Disk](https://disk.yandex.com/)
+  - [x] [百度云](http://pan.baidu.com/)
 - [x] 部署方便，开箱即用
 - [x] 文件预览（PDF、markdown、代码、纯文本……）
 - [x] 画廊模式下的图像预览
@@ -49,11 +52,11 @@
 - [x] Cloudflare workers 中转
 - [x] 文件/文件夹打包下载
 - [x] 支持视频列表播放和字幕(ass,srt,vtt)
-- [x] 网页上传(可以允许访客上传)，删除，新建文件夹，重命名，移动
+- [x] 网页上传(可以允许访客上传)，删除，新建文件夹，重命名，移动，复制
 
 ## 讨论
 
-一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) ，**issue仅针对错误报告。**
+一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) ，**issue仅针对错误报告和功能请求。**
 
 ## 演示
 
diff --git a/alist.go b/alist.go
index c62f29212eb..e43f4b9d747 100644
--- a/alist.go
+++ b/alist.go
@@ -22,7 +22,7 @@ func Init() bool {
 			log.Errorf(err.Error())
 			return false
 		}
-		log.Infof("current password: %s", pass.Value)
+		fmt.Printf("your password: %s\n", pass.Value)
 		return false
 	}
 	server.InitIndex()
diff --git a/bootstrap/conf.go b/bootstrap/conf.go
index a254b22aac2..88094a8eb76 100644
--- a/bootstrap/conf.go
+++ b/bootstrap/conf.go
@@ -21,28 +21,28 @@ func InitConf() {
 		if !utils.WriteToJson(conf.ConfigFile, conf.Conf) {
 			log.Fatalf("failed to create default config file")
 		}
-		return
-	}
-	config, err := ioutil.ReadFile(conf.ConfigFile)
-	if err != nil {
-		log.Fatalf("reading config file error:%s", err.Error())
-	}
-	conf.Conf = new(conf.Config)
-	err = utils.Json.Unmarshal(config, conf.Conf)
-	if err != nil {
-		log.Fatalf("load config error: %s", err.Error())
-	}
-	log.Debugf("config:%+v", conf.Conf)
-	// update config.json struct
-	confBody, err := utils.Json.MarshalIndent(conf.Conf, "", "  ")
-	if err != nil {
-		log.Fatalf("marshal config error:%s", err.Error())
-	}
-	err = ioutil.WriteFile(conf.ConfigFile, confBody, 0777)
-	if err != nil {
-		log.Fatalf("update config struct error: %s", err.Error())
+	} else {
+		config, err := ioutil.ReadFile(conf.ConfigFile)
+		if err != nil {
+			log.Fatalf("reading config file error:%s", err.Error())
+		}
+		conf.Conf = new(conf.Config)
+		err = utils.Json.Unmarshal(config, conf.Conf)
+		if err != nil {
+			log.Fatalf("load config error: %s", err.Error())
+		}
+		log.Debugf("config:%+v", conf.Conf)
+		// update config.json struct
+		confBody, err := utils.Json.MarshalIndent(conf.Conf, "", "  ")
+		if err != nil {
+			log.Fatalf("marshal config error:%s", err.Error())
+		}
+		err = ioutil.WriteFile(conf.ConfigFile, confBody, 0777)
+		if err != nil {
+			log.Fatalf("update config struct error: %s", err.Error())
+		}
 	}
-	err = os.MkdirAll("data/temp", 0700)
+	err := os.MkdirAll(conf.Conf.TempDir, 0700)
 	if err != nil {
 		log.Fatalf("create temp dir error: %s", err.Error())
 	}
diff --git a/bootstrap/log.go b/bootstrap/log.go
index e8ac6918562..a2548964d51 100644
--- a/bootstrap/log.go
+++ b/bootstrap/log.go
@@ -12,6 +12,9 @@ func InitLog() {
 		log.SetLevel(log.DebugLevel)
 		log.SetReportCaller(true)
 	}
+	if conf.Password {
+		log.SetLevel(log.WarnLevel)
+	}
 	log.SetFormatter(&log.TextFormatter{
 		//DisableColors: true,
 		ForceColors:               true,
@@ -29,4 +32,4 @@ func init() {
 	flag.BoolVar(&conf.Password, "password", false, "print current password")
 	flag.Parse()
 	InitLog()
-}
\ No newline at end of file
+}
diff --git a/bootstrap/setting.go b/bootstrap/setting.go
index fa236f2f0c5..434240f845f 100644
--- a/bootstrap/setting.go
+++ b/bootstrap/setting.go
@@ -3,6 +3,7 @@ package bootstrap
 import (
 	"github.com/Xhofe/alist/conf"
 	"github.com/Xhofe/alist/model"
+	"github.com/Xhofe/alist/utils"
 	log "github.com/sirupsen/logrus"
 	"gorm.io/gorm"
 	"strings"
@@ -27,7 +28,7 @@ func InitSettings() {
 		},
 		{
 			Key:         "password",
-			Value:       "alist",
+			Value:       utils.RandomStr(8),
 			Description: "password",
 			Type:        "string",
 			Access:      model.PRIVATE,
@@ -35,7 +36,7 @@ func InitSettings() {
 		},
 		{
 			Key:         "logo",
-			Value:       "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png",
+			Value:       "https://cdn.jsdelivr.net/gh/alist-org/logo@main/can_circle.svg",
 			Description: "logo",
 			Type:        "string",
 			Access:      model.PUBLIC,
@@ -43,7 +44,7 @@ func InitSettings() {
 		},
 		{
 			Key:         "favicon",
-			Value:       "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png",
+			Value:       "https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg",
 			Description: "favicon",
 			Type:        "string",
 			Access:      model.PUBLIC,
@@ -64,16 +65,23 @@ func InitSettings() {
 			Description: "text type extensions",
 			Group:       model.FRONT,
 		},
+		//{
+		//	Key:         "hide readme file",
+		//	Value:       "true",
+		//	Type:        "bool",
+		//	Description: "hide readme file? ",
+		//	Group:       model.FRONT,
+		//},
 		{
-			Key:         "hide readme file",
-			Value:       "true",
-			Type:        "bool",
-			Description: "hide readme file? ",
+			Key:         "hide files",
+			Value:       "/\\/README.md/i",
+			Type:        "text",
+			Description: "hide files, support RegExp, one per line",
 			Group:       model.FRONT,
 		},
 		{
 			Key:         "music cover",
-			Value:       "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png",
+			Value:       "https://cdn.jsdelivr.net/gh/alist-org/logo@main/circle_center.svg",
 			Description: "music cover image",
 			Type:        "string",
 			Access:      model.PUBLIC,
@@ -157,7 +165,7 @@ func InitSettings() {
 		},
 		{
 			Key:         "WebDAV username",
-			Value:       "alist_admin",
+			Value:       "admin",
 			Description: "WebDAV username",
 			Type:        "string",
 			Access:      model.PRIVATE,
@@ -165,7 +173,7 @@ func InitSettings() {
 		},
 		{
 			Key:         "WebDAV password",
-			Value:       "alist_admin",
+			Value:       utils.RandomStr(8),
 			Description: "WebDAV password",
 			Type:        "string",
 			Access:      model.PRIVATE,
@@ -189,7 +197,7 @@ func InitSettings() {
 		},
 		{
 			Key:         "Visitor WebDAV username",
-			Value:       "alist_visitor",
+			Value:       "guest",
 			Description: "Visitor WebDAV username",
 			Type:        "string",
 			Access:      model.PRIVATE,
@@ -197,7 +205,7 @@ func InitSettings() {
 		},
 		{
 			Key:         "Visitor WebDAV password",
-			Value:       "alist_visitor",
+			Value:       "guest",
 			Description: "Visitor WebDAV password",
 			Type:        "string",
 			Access:      model.PRIVATE,
@@ -235,6 +243,9 @@ func InitSettings() {
 		if err != nil {
 			if err == gorm.ErrRecordNotFound {
 				err = model.SaveSetting(v)
+				if v.Key == "password" {
+					log.Infof("Initial password: %s", v.Value)
+				}
 				if err != nil {
 					log.Fatalf("failed write setting: %s", err.Error())
 				}
diff --git a/conf/config.go b/conf/config.go
index fd82927539a..6910937afeb 100644
--- a/conf/config.go
+++ b/conf/config.go
@@ -30,13 +30,15 @@ type Config struct {
 	Database Database    `json:"database"`
 	Scheme   Scheme      `json:"scheme"`
 	Cache    CacheConfig `json:"cache"`
+	TempDir  string      `json:"temp_dir"`
 }
 
 func DefaultConfig() *Config {
 	return &Config{
 		Address: "0.0.0.0",
 		Port:    5244,
-		Assets:  "zhimg",
+		Assets:  "jsdelivr",
+		TempDir: "data/temp",
 		Database: Database{
 			Type:        "sqlite3",
 			Port:        0,
diff --git a/conf/var.go b/conf/var.go
index a1d72440961..56c43e867ac 100644
--- a/conf/var.go
+++ b/conf/var.go
@@ -39,7 +39,7 @@ var (
 	ImageTypes  = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg", "ico", "swf", "webp"}
 )
 
-var settingsMap = make(map[string]string, 0)
+var settingsMap = make(map[string]string)
 
 func Set(key string, value string) {
 	settingsMap[key] = value
@@ -78,7 +78,7 @@ var (
 		"check parent folder", "check down link", "WebDAV username", "WebDAV password",
 		"Visitor WebDAV username", "Visitor WebDAV password",
 		"default page size", "load type",
-		"ocr api",
+		"ocr api", "favicon",
 	}
 )
 
diff --git a/drivers/123/driver.go b/drivers/123/driver.go
index dc6646a068f..cc623a82000 100644
--- a/drivers/123/driver.go
+++ b/drivers/123/driver.go
@@ -12,7 +12,6 @@ import (
 	"github.com/aws/aws-sdk-go/aws/credentials"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/s3/s3manager"
-	"github.com/gin-gonic/gin"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"io/ioutil"
@@ -58,6 +57,7 @@ func (driver Pan123) Items() []base.Item {
 			Type:     base.TypeSelect,
 			Values:   "name,fileId,updateAt,createAt",
 			Required: true,
+			Default:  "name",
 		},
 		{
 			Name:     "order_direction",
@@ -65,6 +65,7 @@ func (driver Pan123) Items() []base.Item {
 			Type:     base.TypeSelect,
 			Values:   "asc,desc",
 			Required: true,
+			Default:  "asc",
 		},
 	}
 }
@@ -197,9 +198,9 @@ func (driver Pan123) Path(path string, account *model.Account) (*model.File, []m
 	return nil, files, nil
 }
 
-func (driver Pan123) Proxy(c *gin.Context, account *model.Account) {
-	c.Request.Header.Del("origin")
-}
+//func (driver Pan123) Proxy(r *http.Request, account *model.Account) {
+//	r.Header.Del("origin")
+//}
 
 func (driver Pan123) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/139/139.go b/drivers/139/139.go
index 081612cf7d1..fb6a412be6b 100644
--- a/drivers/139/139.go
+++ b/drivers/139/139.go
@@ -17,7 +17,7 @@ import (
 func (driver Cloud139) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
 	url := "https://yun.139.com" + pathname
 	req := base.RestyClient.R()
-	randStr := randomStr(16)
+	randStr := utils.RandomStr(16)
 	ts := time.Now().Format("2006-01-02 15:04:05")
 	log.Debugf("%+v", data)
 	body, err := utils.Json.Marshal(data)
diff --git a/drivers/139/driver.go b/drivers/139/driver.go
index 27b4ca02f1d..25aa27b9dcf 100644
--- a/drivers/139/driver.go
+++ b/drivers/139/driver.go
@@ -7,7 +7,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"math"
@@ -163,9 +162,9 @@ func (driver Cloud139) Path(path string, account *model.Account) (*model.File, [
 	return nil, files, nil
 }
 
-func (driver Cloud139) Proxy(c *gin.Context, account *model.Account) {
-
-}
+//func (driver Cloud139) Proxy(r *http.Request, account *model.Account) {
+//
+//}
 
 func (driver Cloud139) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/139/util.go b/drivers/139/util.go
index 0e2a5b79fb7..8d513ab3434 100644
--- a/drivers/139/util.go
+++ b/drivers/139/util.go
@@ -5,7 +5,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"math/rand"
 	"net/url"
 	"sort"
 	"strconv"
@@ -13,16 +12,6 @@ import (
 	"time"
 )
 
-func randomStr(n int) string {
-	builder := strings.Builder{}
-	t := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
-	for i := 0; i < n; i++ {
-		r := rand.Intn(len(t))
-		builder.WriteString(t[r : r+1])
-	}
-	return builder.String()
-}
-
 func encodeURIComponent(str string) string {
 	r := url.QueryEscape(str)
 	r = strings.Replace(r, "+", "%20", -1)
diff --git a/drivers/189/189.go b/drivers/189/189.go
index 724f0fc5f7a..c1ec9e9037e 100644
--- a/drivers/189/189.go
+++ b/drivers/189/189.go
@@ -2,16 +2,9 @@ package _89
 
 import (
 	"bytes"
-	"crypto/aes"
-	"crypto/hmac"
 	"crypto/md5"
-	"crypto/rand"
-	"crypto/rsa"
-	"crypto/sha1"
-	"crypto/x509"
 	"encoding/base64"
 	"encoding/hex"
-	"encoding/pem"
 	"errors"
 	"fmt"
 	"github.com/Xhofe/alist/conf"
@@ -19,14 +12,11 @@ import (
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
 	"github.com/go-resty/resty/v2"
-	"github.com/google/uuid"
 	jsoniter "github.com/json-iterator/go"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"math"
-	mathRand "math/rand"
 	"net/http"
-	"net/url"
 	"path/filepath"
 	"regexp"
 	"strconv"
@@ -35,6 +25,7 @@ import (
 )
 
 var client189Map map[string]*resty.Client
+var infoMap = make(map[string]Rsa)
 
 func (driver Cloud189) getClient(account *model.Account) (*resty.Client, error) {
 	client, ok := client189Map[account.Name]
@@ -116,6 +107,7 @@ func (driver Cloud189) Login(account *model.Account) error {
 		//cookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
 		client = resty.New()
 		//client.SetCookieJar(cookieJar)
+		client.SetTimeout(base.DefaultTimeout)
 		client.SetRetryCount(3)
 		client.SetHeader("Referer", "https://cloud.189.cn/")
 	}
@@ -158,7 +150,7 @@ func (driver Cloud189) Login(account *model.Account) error {
 		u := "https://open.e.189.cn/api/logbox/oauth2/picCaptcha.do?token=" + vCodeID + timeStamp
 		imgRes, err := client.R().SetHeaders(map[string]string{
 			"User-Agent":     "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0",
-			"Referer":        "https://open.e.pan.cn/",
+			"Referer":        "https://open.e.189.cn/api/logbox/oauth2/unifyAccountLogin.do",
 			"Sec-Fetch-Dest": "image",
 			"Sec-Fetch-Mode": "no-cors",
 			"Sec-Fetch-Site": "same-origin",
@@ -176,9 +168,10 @@ func (driver Cloud189) Login(account *model.Account) error {
 			return errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString())
 		}
 		vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString()
+		log.Debugln("code: ", vCodeRS)
 	}
-	userRsa := RsaEncode([]byte(account.Username), jRsakey)
-	passwordRsa := RsaEncode([]byte(account.Password), jRsakey)
+	userRsa := RsaEncode([]byte(account.Username), jRsakey, true)
+	passwordRsa := RsaEncode([]byte(account.Password), jRsakey, true)
 	url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
 	var loginResp LoginResp
 	res, err := client.R().
@@ -222,39 +215,6 @@ func (driver Cloud189) Login(account *model.Account) error {
 	return nil
 }
 
-type Cloud189Error struct {
-	ErrorCode string `json:"errorCode"`
-	ErrorMsg  string `json:"errorMsg"`
-}
-
-type Cloud189File struct {
-	Id         int64  `json:"id"`
-	LastOpTime string `json:"lastOpTime"`
-	Name       string `json:"name"`
-	Size       int64  `json:"size"`
-	Icon       struct {
-		SmallUrl string `json:"smallUrl"`
-		//LargeUrl string `json:"largeUrl"`
-	} `json:"icon"`
-	Url string `json:"url"`
-}
-
-type Cloud189Folder struct {
-	Id         int64  `json:"id"`
-	LastOpTime string `json:"lastOpTime"`
-	Name       string `json:"name"`
-}
-
-type Cloud189Files struct {
-	ResCode    int    `json:"res_code"`
-	ResMessage string `json:"res_message"`
-	FileListAO struct {
-		Count      int              `json:"count"`
-		FileList   []Cloud189File   `json:"fileList"`
-		FolderList []Cloud189Folder `json:"folderList"`
-	} `json:"fileListAO"`
-}
-
 func (driver Cloud189) isFamily(account *model.Account) bool {
 	return account.InternalType == "Family"
 }
@@ -344,7 +304,7 @@ func (driver Cloud189) Request(url string, method int, query, form map[string]st
 	if err != nil {
 		return nil, err
 	}
-	log.Debug(res.String())
+	//log.Debug(res.String())
 	if e.ErrorCode != "" {
 		if e.ErrorCode == "InvalidSessionKey" {
 			err = driver.Login(account)
@@ -361,61 +321,198 @@ func (driver Cloud189) Request(url string, method int, query, form map[string]st
 }
 
 func (driver Cloud189) GetSessionKey(account *model.Account) (string, error) {
+	//info, ok := infoMap[account.Name]
+	//if !ok {
+	//	info = Info{}
+	//	infoMap[account.Name] = info
+	//} else {
+	//	log.Debugf("hit")
+	//}
+	//if info.SessionKey != "" {
+	//	return info.SessionKey, nil
+	//}
 	resp, err := driver.Request("https://cloud.189.cn/v2/getUserBriefInfo.action", base.Get, nil, nil, nil, account)
 	if err != nil {
 		return "", err
 	}
-	return jsoniter.Get(resp, "sessionKey").ToString(), nil
+	sessionKey := jsoniter.Get(resp, "sessionKey").ToString()
+	//info.SessionKey = sessionKey
+	return sessionKey, nil
 }
 
 func (driver Cloud189) GetResKey(account *model.Account) (string, string, error) {
+	rsa, ok := infoMap[account.Name]
+	if !ok {
+		rsa = Rsa{}
+		infoMap[account.Name] = rsa
+	}
+	now := time.Now().UnixMilli()
+	if rsa.Expire > now {
+		return rsa.PubKey, rsa.PkId, nil
+	}
 	resp, err := driver.Request("https://cloud.189.cn/api/security/generateRsaKey.action", base.Get, nil, nil, nil, account)
 	if err != nil {
 		return "", "", err
 	}
-	return jsoniter.Get(resp, "pubKey").ToString(), jsoniter.Get(resp, "pkId").ToString(), nil
+	pubKey, pkId := jsoniter.Get(resp, "pubKey").ToString(), jsoniter.Get(resp, "pkId").ToString()
+	rsa.PubKey, rsa.PkId = pubKey, pkId
+	rsa.Expire = jsoniter.Get(resp, "expire").ToInt64()
+	return pubKey, pkId, nil
 }
 
-func (driver Cloud189) UploadRequest(url string, form map[string]string, account *model.Account) ([]byte, error) {
-	sessionKey, err := driver.GetSessionKey(account)
+//func (driver Cloud189) UploadRequest1(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) {
+//	//sessionKey, err := driver.GetSessionKey(account)
+//	//if err != nil {
+//	//	return nil, err
+//	//}
+//	sessionKey := account.DriveId
+//	pubKey, pkId, err := driver.GetResKey(account)
+//	log.Debugln(sessionKey, pubKey, pkId)
+//	if err != nil {
+//		return nil, err
+//	}
+//	xRId := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
+//	pkey := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")[0 : 16+int(16*mathRand.Float32())]
+//	params := hex.EncodeToString(AesEncrypt([]byte(qs(form)), []byte(pkey[0:16])))
+//	date := strconv.FormatInt(time.Now().Unix(), 10)
+//	a := make(url.Values)
+//	a.Set("SessionKey", sessionKey)
+//	a.Set("Operate", http.MethodGet)
+//	a.Set("RequestURI", uri)
+//	a.Set("Date", date)
+//	a.Set("params", params)
+//	signature := hex.EncodeToString(SHA1(EncodeParam(a), pkey))
+//	encryptionText := RsaEncode([]byte(pkey), pubKey, false)
+//	headers := map[string]string{
+//		"signature":      signature,
+//		"sessionKey":     sessionKey,
+//		"encryptionText": encryptionText,
+//		"pkId":           pkId,
+//		"x-request-id":   xRId,
+//		"x-request-date": date,
+//	}
+//	req := base.RestyClient.R().SetHeaders(headers).SetQueryParam("params", params)
+//	if resp != nil {
+//		req.SetResult(resp)
+//	}
+//	res, err := req.Get("https://upload.cloud.189.cn" + uri)
+//	if err != nil {
+//		return nil, err
+//	}
+//	//log.Debug(res.String())
+//	data := res.Body()
+//	if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
+//		return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
+//	}
+//	return data, nil
+//}
+//
+//func (driver Cloud189) UploadRequest2(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) {
+//	c := strconv.FormatInt(time.Now().UnixMilli(), 10)
+//	r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
+//	l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
+//	l = l[0 : 16+int(16*mathRand.Float32())]
+//
+//	e := qs(form)
+//	data := AesEncrypt([]byte(e), []byte(l[0:16]))
+//	h := hex.EncodeToString(data)
+//
+//	sessionKey := account.DriveId
+//	a := make(url.Values)
+//	a.Set("SessionKey", sessionKey)
+//	a.Set("Operate", http.MethodGet)
+//	a.Set("RequestURI", uri)
+//	a.Set("Date", c)
+//	a.Set("params", h)
+//	g := SHA1(EncodeParam(a), l)
+//
+//	pubKey, pkId, err := driver.GetResKey(account)
+//	if err != nil {
+//		return nil, err
+//	}
+//	b := RsaEncode([]byte(l), pubKey, false)
+//	client, err := driver.getClient(account)
+//	if err != nil {
+//		return nil, err
+//	}
+//	req := client.R()
+//	req.Header.Set("accept", "application/json;charset=UTF-8")
+//	req.Header.Set("SessionKey", sessionKey)
+//	req.Header.Set("Signature", hex.EncodeToString(g))
+//	req.Header.Set("X-Request-Date", c)
+//	req.Header.Set("X-Request-ID", r)
+//	req.Header.Set("EncryptionText", b)
+//	req.Header.Set("PkId", pkId)
+//	if resp != nil {
+//		req.SetResult(resp)
+//	}
+//	res, err := req.Get("https://upload.cloud.189.cn" + uri + "?params=" + h)
+//	if err != nil {
+//		return nil, err
+//	}
+//	//log.Debug(res.String())
+//	data = res.Body()
+//	if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
+//		return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
+//	}
+//	return data, nil
+//}
+
+func (driver Cloud189) UploadRequest(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) {
+	c := strconv.FormatInt(time.Now().UnixMilli(), 10)
+	r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
+	l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
+	l = l[0 : 16+int(16*utils.Rand.Float32())]
+
+	e := qs(form)
+	data := AesEncrypt([]byte(e), []byte(l[0:16]))
+	h := hex.EncodeToString(data)
+
+	sessionKey := account.DriveId
+	signature := hmacSha1(fmt.Sprintf("SessionKey=%s&Operate=GET&RequestURI=%s&Date=%s&params=%s", sessionKey, uri, c, h), l)
+
+	pubKey, pkId, err := driver.GetResKey(account)
 	if err != nil {
 		return nil, err
 	}
-	pubKey, pkId, err := driver.GetResKey(account)
+	b := RsaEncode([]byte(l), pubKey, false)
+	client, err := driver.getClient(account)
 	if err != nil {
 		return nil, err
 	}
-	xRId := uuid.New().String()
-	pkey := strings.ReplaceAll(xRId, "-", "")[:mathRand.Intn(16)+16]
-	params := aesEncrypt(qs(form), pkey[:16])
-	date := strconv.FormatInt(time.Now().Unix(), 10)
-	signature := hmacSha1(fmt.Sprintf("SessionKey=%s&Operate=GET&RequestURI=%s&Date=%s&params=%s", sessionKey, url, date, params), pkey)
-	encryptionText := RsaEncode([]byte(pkey), pubKey)
-	headers := map[string]string{
-		"signature":      signature,
-		"sessionKey":     sessionKey,
-		"encryptionText": encryptionText,
-		"pkId":           pkId,
-		"x-request-id":   xRId,
-		"x-request-date": date,
-		"origin":         "https://cloud.189.cn",
-		"referer":        "https://cloud.189.cn/",
-	}
-	log.Debugf("%+v\n%s", headers, params)
-	res, err := base.RestyClient.R().SetHeaders(headers).SetQueryParam("params", params).Get("https://upload.cloud.189.cn" + url)
+	req := client.R()
+	req.Header.Set("accept", "application/json;charset=UTF-8")
+	req.Header.Set("SessionKey", sessionKey)
+	req.Header.Set("Signature", signature)
+	req.Header.Set("X-Request-Date", c)
+	req.Header.Set("X-Request-ID", r)
+	req.Header.Set("EncryptionText", b)
+	req.Header.Set("PkId", pkId)
+	if resp != nil {
+		req.SetResult(resp)
+	}
+	res, err := req.Get("https://upload.cloud.189.cn" + uri + "?params=" + h)
 	if err != nil {
 		return nil, err
 	}
-	log.Debug(res.String())
-	data := res.Body()
+	//log.Debug(res.String())
+	data = res.Body()
 	if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
-		return nil, errors.New(jsoniter.Get(data, "msg").ToString())
+		return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
 	}
 	return data, nil
 }
 
-// Upload Error: decrypt encryptionText failed
+// NewUpload Error: signature check false
 func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account) error {
+	sessionKey, err := driver.GetSessionKey(account)
+	if err != nil {
+		account.Status = err.Error()
+	} else {
+		account.Status = "work"
+		account.DriveId = sessionKey
+	}
+	_ = model.SaveAccount(account)
 	const DEFAULT uint64 = 10485760
 	var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
 	var finish uint64 = 0
@@ -428,15 +525,18 @@ func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account)
 	}
 	res, err := driver.UploadRequest("/person/initMultiUpload", map[string]string{
 		"parentFolderId": parentFile.Id,
-		"fileName":       file.Name,
+		"fileName":       encode(file.Name),
 		"fileSize":       strconv.FormatInt(int64(file.Size), 10),
 		"sliceSize":      strconv.FormatInt(int64(DEFAULT), 10),
 		"lazyCheck":      "1",
-	}, account)
+	}, account, nil)
 	if err != nil {
 		return err
 	}
-	uploadFileId := jsoniter.Get(res, "data.uploadFileId").ToString()
+	uploadFileId := jsoniter.Get(res, "data", "uploadFileId").ToString()
+	//_, err = driver.UploadRequest("/person/getUploadedPartsInfo", map[string]string{
+	//	"uploadFileId": uploadFileId,
+	//}, account, nil)
 	var i int64
 	var byteSize uint64
 	md5s := make([]string, 0)
@@ -446,180 +546,82 @@ func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account)
 		if DEFAULT < byteSize {
 			byteSize = DEFAULT
 		}
-		log.Debugf("%d,%d", byteSize, finish)
+		//log.Debugf("%d,%d", byteSize, finish)
 		byteData := make([]byte, byteSize)
 		n, err := io.ReadFull(file, byteData)
-		log.Debug(err, n)
+		//log.Debug(err, n)
 		if err != nil {
 			return err
 		}
 		finish += uint64(n)
 		md5Bytes := getMd5(byteData)
-		md5Str := hex.EncodeToString(md5Bytes)
+		md5Hex := hex.EncodeToString(md5Bytes)
 		md5Base64 := base64.StdEncoding.EncodeToString(md5Bytes)
-		md5s = append(md5s, md5Str)
+		md5s = append(md5s, strings.ToUpper(md5Hex))
 		md5Sum.Write(byteData)
+		//log.Debugf("md5Bytes: %+v,md5Str:%s,md5Base64:%s", md5Bytes, md5Hex, md5Base64)
+		var resp UploadUrlsResp
 		res, err = driver.UploadRequest("/person/getMultiUploadUrls", map[string]string{
 			"partInfo":     fmt.Sprintf("%s-%s", strconv.FormatInt(i, 10), md5Base64),
 			"uploadFileId": uploadFileId,
-		}, account)
+		}, account, &resp)
 		if err != nil {
 			return err
 		}
-		uploadData := jsoniter.Get(res, "uploadUrls.partNumber_"+strconv.FormatInt(i, 10))
-		headers := strings.Split(uploadData.Get("requestHeader").ToString(), "&")
-		req, err := http.NewRequest("PUT", uploadData.Get("requestURL").ToString(), bytes.NewBuffer(byteData))
-		if err != nil {
-			return err
-		}
-		for _, header := range headers {
-			kv := strings.Split(header, "=")
-			req.Header.Set(kv[0], strings.Join(kv[1:], "="))
+		uploadData := resp.UploadUrls["partNumber_"+strconv.FormatInt(i, 10)]
+		log.Debugf("uploadData: %+v", uploadData)
+		requestURL := uploadData.RequestURL
+		uploadHeaders := strings.Split(decodeURIComponent(uploadData.RequestHeader), "&")
+		req, _ := http.NewRequest(http.MethodPut, requestURL, bytes.NewReader(byteData))
+		for _, v := range uploadHeaders {
+			i := strings.Index(v, "=")
+			req.Header.Set(v[0:i], v[i+1:])
 		}
-		res, err := base.HttpClient.Do(req)
+
+		r, err := base.HttpClient.Do(req)
+		log.Debugf("%+v %+v", r, r.Request.Header)
 		if err != nil {
 			return err
 		}
-		log.Debugf("%+v", res)
 	}
-	id := md5Sum.Sum(nil)
+	fileMd5 := hex.EncodeToString(md5Sum.Sum(nil))
+	sliceMd5 := fileMd5
+	if file.GetSize() > DEFAULT {
+		sliceMd5 = utils.GetMD5Encode(strings.Join(md5s, "\n"))
+	}
 	res, err = driver.UploadRequest("/person/commitMultiUploadFile", map[string]string{
 		"uploadFileId": uploadFileId,
-		"fileMd5":      hex.EncodeToString(id),
-		"sliceMd5":     utils.GetMD5Encode(strings.Join(md5s, "\n")),
+		"fileMd5":      fileMd5,
+		"sliceMd5":     sliceMd5,
 		"lazyCheck":    "1",
-	}, account)
+	}, account, nil)
+	account.DriveId, _ = driver.GetSessionKey(account)
 	return err
 }
 
-func random() string {
-	return fmt.Sprintf("0.%17v", mathRand.New(mathRand.NewSource(time.Now().UnixNano())).Int63n(100000000000000000))
-}
-
-func RsaEncode(origData []byte, j_rsakey string) string {
-	publicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + j_rsakey + "\n-----END PUBLIC KEY-----")
-	block, _ := pem.Decode(publicKey)
-	pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
-	pub := pubInterface.(*rsa.PublicKey)
-	b, err := rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
+func (driver Cloud189) OldUpload(file *model.FileStream, account *model.Account) error {
+	//return base.ErrNotImplement
+	client, err := driver.getClient(account)
 	if err != nil {
-		log.Errorf("err: %s", err.Error())
-	}
-	return b64tohex(base64.StdEncoding.EncodeToString(b))
-}
-
-var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
-
-var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"
-
-func int2char(a int) string {
-	return strings.Split(BI_RM, "")[a]
-}
-
-func b64tohex(a string) string {
-	d := ""
-	e := 0
-	c := 0
-	for i := 0; i < len(a); i++ {
-		m := strings.Split(a, "")[i]
-		if m != "=" {
-			v := strings.Index(b64map, m)
-			if 0 == e {
-				e = 1
-				d += int2char(v >> 2)
-				c = 3 & v
-			} else if 1 == e {
-				e = 2
-				d += int2char(c<<2 | v>>4)
-				c = 15 & v
-			} else if 2 == e {
-				e = 3
-				d += int2char(c)
-				d += int2char(v >> 2)
-				c = 3 & v
-			} else {
-				e = 0
-				d += int2char(c<<2 | v>>4)
-				d += int2char(15 & v)
-			}
-		}
-	}
-	if e == 1 {
-		d += int2char(c << 2)
-	}
-	return d
-}
-
-func qs(form map[string]string) string {
-	strList := make([]string, 0)
-	for k, v := range form {
-		strList = append(strList, fmt.Sprintf("%s=%s", k, url.QueryEscape(v)))
-	}
-	return strings.Join(strList, "&")
-}
-
-func aesEncrypt(data, key string) string {
-	encrypted := AesEncryptECB([]byte(data), []byte(key))
-	//return string(encrypted)
-	return hex.EncodeToString(encrypted)
-}
-
-func hmacSha1(data string, secret string) string {
-	h := hmac.New(sha1.New, []byte(secret))
-	h.Write([]byte(data))
-	return hex.EncodeToString(h.Sum(nil))
-}
-
-func AesEncryptECB(origData []byte, key []byte) (encrypted []byte) {
-	cipher, _ := aes.NewCipher(generateKey(key))
-	length := (len(origData) + aes.BlockSize) / aes.BlockSize
-	plain := make([]byte, length*aes.BlockSize)
-	copy(plain, origData)
-	pad := byte(len(plain) - len(origData))
-	for i := len(origData); i < len(plain); i++ {
-		plain[i] = pad
-	}
-	encrypted = make([]byte, len(plain))
-	// 分组分块加密
-	for bs, be := 0, cipher.BlockSize(); bs <= len(origData); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
-		cipher.Encrypt(encrypted[bs:be], plain[bs:be])
+		return err
 	}
-
-	return encrypted
-}
-func AesDecryptECB(encrypted []byte, key []byte) (decrypted []byte) {
-	cipher, _ := aes.NewCipher(generateKey(key))
-	decrypted = make([]byte, len(encrypted))
-	//
-	for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
-		cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
+	parentFile, err := driver.File(file.ParentPath, account)
+	if err != nil {
+		return err
 	}
-
-	trim := 0
-	if len(decrypted) > 0 {
-		trim = len(decrypted) - int(decrypted[len(decrypted)-1])
+	// api refer to PanIndex
+	res, err := client.R().SetMultipartFormData(map[string]string{
+		"parentId":   parentFile.Id,
+		"sessionKey": account.DriveId,
+		"opertype":   "1",
+		"fname":      file.GetFileName(),
+	}).SetMultipartField("Filedata", file.GetFileName(), file.GetMIMEType(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction")
+	if err != nil {
+		return err
 	}
-
-	return decrypted[:trim]
-}
-func generateKey(key []byte) (genKey []byte) {
-	genKey = make([]byte, 16)
-	copy(genKey, key)
-	for i := 16; i < len(key); {
-		for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
-			genKey[j] ^= key[i]
-		}
+	if jsoniter.Get(res.Body(), "MD5").ToString() != "" {
+		return nil
 	}
-	return genKey
-}
-
-func getMd5(data []byte) []byte {
-	h := md5.New()
-	h.Write(data)
-	return h.Sum(nil)
-}
-
-func init() {
-	base.RegisterDriver(&Cloud189{})
-	client189Map = make(map[string]*resty.Client, 0)
+	log.Debugf(res.String())
+	return errors.New(res.String())
 }
diff --git a/drivers/189/driver.go b/drivers/189/driver.go
index 8dbe337e581..af15cf3c458 100644
--- a/drivers/189/driver.go
+++ b/drivers/189/driver.go
@@ -1,14 +1,11 @@
 package _89
 
 import (
-	"errors"
 	"fmt"
 	"github.com/Xhofe/alist/conf"
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
-	jsoniter "github.com/json-iterator/go"
 	log "github.com/sirupsen/logrus"
 	"path/filepath"
 )
@@ -174,7 +171,12 @@ func (driver Cloud189) Link(args base.Args, account *model.Account) (*base.Link,
 	if err != nil {
 		return nil, err
 	}
-	link := base.Link{}
+	link := base.Link{
+		Headers: []base.Header{
+			{Name: "User-Agent", Value: base.UserAgent},
+			{Name: "Authorization", Value: ""},
+		},
+	}
 	if res.StatusCode() == 302 {
 		link.Url = res.Header().Get("location")
 	} else {
@@ -200,9 +202,9 @@ func (driver Cloud189) Path(path string, account *model.Account) (*model.File, [
 	return nil, files, nil
 }
 
-func (driver Cloud189) Proxy(ctx *gin.Context, account *model.Account) {
-	ctx.Request.Header.Del("Origin")
-}
+//func (driver Cloud189) Proxy(r *http.Request, account *model.Account) {
+//	r.Header.Del("Origin")
+//}
 
 func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
@@ -350,29 +352,8 @@ func (driver Cloud189) Upload(file *model.FileStream, account *model.Account) er
 	if file == nil {
 		return base.ErrEmptyFile
 	}
-	client, err := driver.getClient(account)
-	if err != nil {
-		return err
-	}
-	parentFile, err := driver.File(file.ParentPath, account)
-	if err != nil {
-		return err
-	}
-	// api refer to PanIndex
-	res, err := client.R().SetMultipartFormData(map[string]string{
-		"parentId":   parentFile.Id,
-		"sessionKey": account.DriveId,
-		"opertype":   "1",
-		"fname":      file.GetFileName(),
-	}).SetMultipartField("Filedata", file.GetFileName(), file.GetMIMEType(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction")
-	if err != nil {
-		return err
-	}
-	if jsoniter.Get(res.Body(), "MD5").ToString() != "" {
-		return nil
-	}
-	log.Debugf(res.String())
-	return errors.New(res.String())
+	return driver.NewUpload(file, account)
+	//return driver.OldUpload(file, account)
 }
 
 var _ base.Driver = (*Cloud189)(nil)
diff --git a/drivers/189/types.go b/drivers/189/types.go
new file mode 100644
index 00000000000..253321c2aa5
--- /dev/null
+++ b/drivers/189/types.go
@@ -0,0 +1,55 @@
+package _89
+
+type Cloud189Error struct {
+	ErrorCode string `json:"errorCode"`
+	ErrorMsg  string `json:"errorMsg"`
+}
+
+type Cloud189File struct {
+	Id         int64  `json:"id"`
+	LastOpTime string `json:"lastOpTime"`
+	Name       string `json:"name"`
+	Size       int64  `json:"size"`
+	Icon       struct {
+		SmallUrl string `json:"smallUrl"`
+		//LargeUrl string `json:"largeUrl"`
+	} `json:"icon"`
+	Url string `json:"url"`
+}
+
+type Cloud189Folder struct {
+	Id         int64  `json:"id"`
+	LastOpTime string `json:"lastOpTime"`
+	Name       string `json:"name"`
+}
+
+type Cloud189Files struct {
+	ResCode    int    `json:"res_code"`
+	ResMessage string `json:"res_message"`
+	FileListAO struct {
+		Count      int              `json:"count"`
+		FileList   []Cloud189File   `json:"fileList"`
+		FolderList []Cloud189Folder `json:"folderList"`
+	} `json:"fileListAO"`
+}
+
+type UploadUrlsResp struct {
+	Code       string          `json:"code"`
+	UploadUrls map[string]Part `json:"uploadUrls"`
+}
+
+type Part struct {
+	RequestURL    string `json:"requestURL"`
+	RequestHeader string `json:"requestHeader"`
+}
+
+//type Info struct {
+//	SessionKey string
+//	Rsa        Rsa
+//}
+
+type Rsa struct {
+	Expire int64  `json:"expire"`
+	PkId   string `json:"pkId"`
+	PubKey string `json:"pubKey"`
+}
diff --git a/drivers/189/util.go b/drivers/189/util.go
new file mode 100644
index 00000000000..c079af879d8
--- /dev/null
+++ b/drivers/189/util.go
@@ -0,0 +1,199 @@
+package _89
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/hmac"
+	"crypto/md5"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha1"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/pem"
+	"fmt"
+	"github.com/Xhofe/alist/drivers/base"
+	"github.com/Xhofe/alist/utils"
+	"github.com/go-resty/resty/v2"
+	log "github.com/sirupsen/logrus"
+	"net/url"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+func random() string {
+	return fmt.Sprintf("0.%17v", utils.Rand.Int63n(100000000000000000))
+}
+
+func RsaEncode(origData []byte, j_rsakey string, hex bool) string {
+	publicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + j_rsakey + "\n-----END PUBLIC KEY-----")
+	block, _ := pem.Decode(publicKey)
+	pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
+	pub := pubInterface.(*rsa.PublicKey)
+	b, err := rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
+	if err != nil {
+		log.Errorf("err: %s", err.Error())
+	}
+	res := base64.StdEncoding.EncodeToString(b)
+	if hex {
+		return b64tohex(res)
+	}
+	return res
+}
+
+var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"
+
+func int2char(a int) string {
+	return strings.Split(BI_RM, "")[a]
+}
+
+func b64tohex(a string) string {
+	d := ""
+	e := 0
+	c := 0
+	for i := 0; i < len(a); i++ {
+		m := strings.Split(a, "")[i]
+		if m != "=" {
+			v := strings.Index(b64map, m)
+			if 0 == e {
+				e = 1
+				d += int2char(v >> 2)
+				c = 3 & v
+			} else if 1 == e {
+				e = 2
+				d += int2char(c<<2 | v>>4)
+				c = 15 & v
+			} else if 2 == e {
+				e = 3
+				d += int2char(c)
+				d += int2char(v >> 2)
+				c = 3 & v
+			} else {
+				e = 0
+				d += int2char(c<<2 | v>>4)
+				d += int2char(15 & v)
+			}
+		}
+	}
+	if e == 1 {
+		d += int2char(c << 2)
+	}
+	return d
+}
+
+func qs(form map[string]string) string {
+	f := make(url.Values)
+	for k, v := range form {
+		f.Set(k, v)
+	}
+	return EncodeParam(f)
+	//strList := make([]string, 0)
+	//for k, v := range form {
+	//	strList = append(strList, fmt.Sprintf("%s=%s", k, url.QueryEscape(v)))
+	//}
+	//return strings.Join(strList, "&")
+}
+
+func EncodeParam(v url.Values) string {
+	if v == nil {
+		return ""
+	}
+	var buf strings.Builder
+	keys := make([]string, 0, len(v))
+	for k := range v {
+		keys = append(keys, k)
+	}
+	for _, k := range keys {
+		vs := v[k]
+		for _, v := range vs {
+			if buf.Len() > 0 {
+				buf.WriteByte('&')
+			}
+			buf.WriteString(k)
+			buf.WriteByte('=')
+			//if k == "fileName" {
+			//	buf.WriteString(encode(v))
+			//} else {
+			buf.WriteString(v)
+			//}
+		}
+	}
+	return buf.String()
+}
+
+func encode(str string) string {
+	//str = strings.ReplaceAll(str, "%", "%25")
+	//str = strings.ReplaceAll(str, "&", "%26")
+	//str = strings.ReplaceAll(str, "+", "%2B")
+	//return str
+	return url.QueryEscape(str)
+}
+
+func AesEncrypt(data, key []byte) []byte {
+	block, _ := aes.NewCipher(key)
+	if block == nil {
+		return []byte{}
+	}
+	data = PKCS7Padding(data, block.BlockSize())
+	decrypted := make([]byte, len(data))
+	size := block.BlockSize()
+	for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
+		block.Encrypt(decrypted[bs:be], data[bs:be])
+	}
+	return decrypted
+}
+
+func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padtext...)
+}
+
+func hmacSha1(data string, secret string) string {
+	h := hmac.New(sha1.New, []byte(secret))
+	h.Write([]byte(data))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+func getMd5(data []byte) []byte {
+	h := md5.New()
+	h.Write(data)
+	return h.Sum(nil)
+}
+
+func init() {
+	base.RegisterDriver(&Cloud189{})
+	client189Map = make(map[string]*resty.Client)
+}
+
+func decodeURIComponent(str string) string {
+	r, _ := url.PathUnescape(str)
+	//r = strings.ReplaceAll(r, " ", "+")
+	return r
+}
+
+func Random(v string) string {
+	reg := regexp.MustCompilePOSIX("[xy]")
+	data := reg.ReplaceAllFunc([]byte(v), func(msg []byte) []byte {
+		var i int64
+		t := int64(16 * utils.Rand.Float32())
+		if msg[0] == 120 {
+			i = t
+		} else {
+			i = 3&t | 8
+		}
+		return []byte(strconv.FormatInt(i, 16))
+	})
+	return string(data)
+}
+
+//func SHA1(v, l string) []byte {
+//	key := []byte(l)
+//	mac := hmac.New(sha1.New, key)
+//	mac.Write([]byte(v))
+//	return mac.Sum(nil)
+//}
diff --git a/drivers/alidrive/alidrive.go b/drivers/alidrive/alidrive.go
index a0b6fdd3934..2acb0163e16 100644
--- a/drivers/alidrive/alidrive.go
+++ b/drivers/alidrive/alidrive.go
@@ -236,6 +236,7 @@ func (driver AliDrive) batch(srcId, dstId string, account *model.Account) error
 func init() {
 	base.RegisterDriver(&AliDrive{})
 	aliClient.
+		SetTimeout(base.DefaultTimeout).
 		SetRetryCount(3).
 		SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36").
 		SetHeader("content-type", "application/json").
diff --git a/drivers/alidrive/driver.go b/drivers/alidrive/driver.go
index 85d80d7ef9a..c8ca7f2ad7c 100644
--- a/drivers/alidrive/driver.go
+++ b/drivers/alidrive/driver.go
@@ -8,7 +8,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	"github.com/robfig/cron/v3"
 	log "github.com/sirupsen/logrus"
 	"io"
@@ -191,6 +190,12 @@ func (driver AliDrive) Link(args base.Args, account *model.Account) (*base.Link,
 		return nil, fmt.Errorf("%s", e.Message)
 	}
 	return &base.Link{
+		Headers: []base.Header{
+			{
+				Name:  "Referer",
+				Value: "https://www.aliyundrive.com/",
+			},
+		},
 		Url: resp["url"].(string),
 	}, nil
 }
@@ -212,10 +217,10 @@ func (driver AliDrive) Path(path string, account *model.Account) (*model.File, [
 	return nil, files, nil
 }
 
-func (driver AliDrive) Proxy(c *gin.Context, account *model.Account) {
-	c.Request.Header.Del("Origin")
-	c.Request.Header.Set("Referer", "https://www.aliyundrive.com/")
-}
+//func (driver AliDrive) Proxy(r *http.Request, account *model.Account) {
+//	r.Header.Del("Origin")
+//	r.Header.Set("Referer", "https://www.aliyundrive.com/")
+//}
 
 func (driver AliDrive) Preview(path string, account *model.Account) (interface{}, error) {
 	file, err := driver.GetFile(path, account)
diff --git a/drivers/alist/driver.go b/drivers/alist/driver.go
index 044cba70f94..3919ff5a7e3 100644
--- a/drivers/alist/driver.go
+++ b/drivers/alist/driver.go
@@ -7,7 +7,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	"path/filepath"
 	"strings"
 	"time"
@@ -148,7 +147,7 @@ func (driver Alist) Path(path string, account *model.Account) (*model.File, []mo
 	return nil, resp.Data.Files, nil
 }
 
-func (driver Alist) Proxy(c *gin.Context, account *model.Account) {}
+//func (driver Alist) Proxy(r *http.Request, account *model.Account) {}
 
 func (driver Alist) Preview(path string, account *model.Account) (interface{}, error) {
 	var resp PathResp
diff --git a/drivers/all.go b/drivers/all.go
index 0910487c29c..b8d88519131 100644
--- a/drivers/all.go
+++ b/drivers/all.go
@@ -6,6 +6,7 @@ import (
 	_ "github.com/Xhofe/alist/drivers/189"
 	_ "github.com/Xhofe/alist/drivers/alidrive"
 	_ "github.com/Xhofe/alist/drivers/alist"
+	_ "github.com/Xhofe/alist/drivers/baidu"
 	"github.com/Xhofe/alist/drivers/base"
 	_ "github.com/Xhofe/alist/drivers/ftp"
 	_ "github.com/Xhofe/alist/drivers/google"
@@ -18,6 +19,7 @@ import (
 	_ "github.com/Xhofe/alist/drivers/shandian"
 	_ "github.com/Xhofe/alist/drivers/teambition"
 	_ "github.com/Xhofe/alist/drivers/webdav"
+	_ "github.com/Xhofe/alist/drivers/yandex"
 	log "github.com/sirupsen/logrus"
 	"strings"
 )
diff --git a/drivers/baidu/baidu.go b/drivers/baidu/baidu.go
new file mode 100644
index 00000000000..bb8d0c08366
--- /dev/null
+++ b/drivers/baidu/baidu.go
@@ -0,0 +1,186 @@
+package baidu
+
+import (
+	"fmt"
+	"github.com/Xhofe/alist/conf"
+	"github.com/Xhofe/alist/drivers/base"
+	"github.com/Xhofe/alist/model"
+	"github.com/Xhofe/alist/utils"
+	"github.com/go-resty/resty/v2"
+	jsoniter "github.com/json-iterator/go"
+	"path"
+	"strconv"
+)
+
+func (driver Baidu) RefreshToken(account *model.Account) error {
+	err := driver.refreshToken(account)
+	if err != nil && err == base.ErrEmptyToken {
+		err = driver.refreshToken(account)
+	}
+	if err != nil {
+		account.Status = err.Error()
+	}
+	_ = model.SaveAccount(account)
+	return err
+}
+
+func (driver Baidu) refreshToken(account *model.Account) error {
+	u := "https://openapi.baidu.com/oauth/2.0/token"
+	var resp base.TokenResp
+	var e TokenErrResp
+	_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetQueryParams(map[string]string{
+		"grant_type":    "refresh_token",
+		"refresh_token": account.RefreshToken,
+		"client_id":     account.ClientId,
+		"client_secret": account.ClientSecret,
+	}).Get(u)
+	if err != nil {
+		return err
+	}
+	if e.Error != "" {
+		return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
+	}
+	if resp.RefreshToken == "" {
+		return base.ErrEmptyToken
+	}
+	account.Status = "work"
+	account.AccessToken, account.RefreshToken = resp.AccessToken, resp.RefreshToken
+	return nil
+}
+
+func (driver Baidu) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
+	u := "https://pan.baidu.com/rest/2.0" + pathname
+	req := base.RestyClient.R()
+	req.SetQueryParam("access_token", 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
+	switch method {
+	case base.Get:
+		res, err = req.Get(u)
+	case base.Post:
+		res, err = req.Post(u)
+	case base.Patch:
+		res, err = req.Patch(u)
+	case base.Delete:
+		res, err = req.Delete(u)
+	case base.Put:
+		res, err = req.Put(u)
+	default:
+		return nil, base.ErrNotSupport
+	}
+	if err != nil {
+		return nil, err
+	}
+	//log.Debug(res.String())
+	errno := jsoniter.Get(res.Body(), "errno").ToInt()
+	if errno != 0 {
+		if errno == -6 {
+			err = driver.RefreshToken(account)
+			if err != nil {
+				return nil, err
+			}
+			return driver.Request(pathname, method, headers, query, form, data, resp, account)
+		}
+		return nil, fmt.Errorf("errno: %d, refer to https://pan.baidu.com/union/doc/", errno)
+	}
+	return res.Body(), nil
+}
+
+func (driver Baidu) Get(pathname string, params map[string]string, resp interface{}, account *model.Account) ([]byte, error) {
+	return driver.Request(pathname, base.Get, nil, params, nil, nil, resp, account)
+}
+
+func (driver Baidu) Post(pathname string, params map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
+	return driver.Request(pathname, base.Post, nil, params, nil, data, resp, account)
+}
+
+func (driver Baidu) manage(opera string, filelist interface{}, account *model.Account) ([]byte, error) {
+	params := map[string]string{
+		"method": "filemanager",
+		"opera":  opera,
+	}
+	marshal, err := utils.Json.Marshal(filelist)
+	if err != nil {
+		return nil, err
+	}
+	data := fmt.Sprintf("async=0&filelist=%s&ondup=newcopy", string(marshal))
+	return driver.Post("/xpan/file", params, data, nil, account)
+}
+
+func (driver Baidu) GetFiles(dir string, account *model.Account) ([]model.File, error) {
+	dir = utils.Join(account.RootFolder, dir)
+	start := 0
+	limit := 200
+	params := map[string]string{
+		"method": "list",
+		"dir":    dir,
+		"web":    "web",
+	}
+	if account.OrderBy != "" {
+		params["order"] = account.OrderBy
+		if account.OrderDirection == "desc" {
+			params["desc"] = "1"
+		}
+	}
+	res := make([]model.File, 0)
+	for {
+		params["start"] = strconv.Itoa(start)
+		params["limit"] = strconv.Itoa(limit)
+		start += limit
+		var resp ListResp
+		_, err := driver.Get("/xpan/file", params, &resp, account)
+		if err != nil {
+			return nil, err
+		}
+		if len(resp.List) == 0 {
+			break
+		}
+		for _, f := range resp.List {
+			file := model.File{
+				Id:        strconv.FormatInt(f.FsId, 10),
+				Name:      f.ServerFilename,
+				Size:      f.Size,
+				Driver:    driver.Config().Name,
+				UpdatedAt: getTime(f.ServerMtime),
+				Thumbnail: f.Thumbs.Url3,
+			}
+			if f.Isdir == 1 {
+				file.Type = conf.FOLDER
+			} else {
+				file.Type = utils.GetFileType(path.Ext(f.ServerFilename))
+			}
+			res = append(res, file)
+		}
+	}
+	return res, nil
+}
+
+func (driver Baidu) create(path string, size uint64, isdir int, uploadid, block_list string, account *model.Account) ([]byte, error) {
+	params := map[string]string{
+		"method": "create",
+	}
+	data := fmt.Sprintf("path=%s&size=%d&isdir=%d", path, size, isdir)
+	if uploadid != "" {
+		data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list)
+	}
+	return driver.Post("/xpan/file", params, data, nil, account)
+}
+
+func init() {
+	base.RegisterDriver(&Baidu{})
+}
diff --git a/drivers/baidu/driver.go b/drivers/baidu/driver.go
new file mode 100644
index 00000000000..f64c56f8cc8
--- /dev/null
+++ b/drivers/baidu/driver.go
@@ -0,0 +1,355 @@
+package baidu
+
+import (
+	"bytes"
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"github.com/Xhofe/alist/conf"
+	"github.com/Xhofe/alist/drivers/base"
+	"github.com/Xhofe/alist/model"
+	"github.com/Xhofe/alist/utils"
+	log "github.com/sirupsen/logrus"
+	"io"
+	"io/ioutil"
+	"math"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+)
+
+type Baidu struct{}
+
+func (driver Baidu) Config() base.DriverConfig {
+	return base.DriverConfig{
+		Name: "Baidu.Disk",
+	}
+}
+
+func (driver Baidu) Items() []base.Item {
+	return []base.Item{
+		{
+			Name:     "refresh_token",
+			Label:    "refresh token",
+			Type:     base.TypeString,
+			Required: true,
+		},
+		{
+			Name:     "root_folder",
+			Label:    "root folder path",
+			Type:     base.TypeString,
+			Default:  "/",
+			Required: true,
+		},
+		{
+			Name:     "order_by",
+			Label:    "order_by",
+			Type:     base.TypeSelect,
+			Default:  "name",
+			Values:   "name,time,size",
+			Required: false,
+		},
+		{
+			Name:     "order_direction",
+			Label:    "order_direction",
+			Type:     base.TypeSelect,
+			Values:   "asc,desc",
+			Default:  "asc",
+			Required: false,
+		},
+		{
+			Name:     "client_id",
+			Label:    "client id",
+			Default:  "iYCeC9g08h5vuP9UqvPHKKSVrKFXGa1v",
+			Type:     base.TypeString,
+			Required: true,
+		},
+		{
+			Name:     "client_secret",
+			Label:    "client secret",
+			Default:  "jXiFMOPVPCWlO2M5CwWQzffpNPaGTRBG",
+			Type:     base.TypeString,
+			Required: true,
+		},
+	}
+}
+
+func (driver Baidu) Save(account *model.Account, old *model.Account) error {
+	return driver.RefreshToken(account)
+}
+
+func (driver Baidu) File(path string, account *model.Account) (*model.File, error) {
+	path = utils.ParsePath(path)
+	if path == "/" {
+		return &model.File{
+			Id:        account.RootFolder,
+			Name:      account.Name,
+			Size:      0,
+			Type:      conf.FOLDER,
+			Driver:    driver.Config().Name,
+			UpdatedAt: account.UpdatedAt,
+		}, nil
+	}
+	dir, name := filepath.Split(path)
+	files, err := driver.Files(dir, account)
+	if err != nil {
+		return nil, err
+	}
+	for _, file := range files {
+		if file.Name == name {
+			return &file, nil
+		}
+	}
+	return nil, base.ErrPathNotFound
+}
+
+func (driver Baidu) Files(path string, account *model.Account) ([]model.File, error) {
+	path = utils.ParsePath(path)
+	cache, err := base.GetCache(path, account)
+	if err == nil {
+		files, _ := cache.([]model.File)
+		return files, nil
+	}
+	files, err := driver.GetFiles(path, account)
+	if err != nil {
+		return nil, err
+	}
+	if len(files) > 0 {
+		_ = base.SetCache(path, files, account)
+	}
+	return files, nil
+}
+
+func (driver Baidu) Link(args base.Args, account *model.Account) (*base.Link, error) {
+	file, err := driver.File(args.Path, account)
+	if err != nil {
+		return nil, err
+	}
+	if file.IsDir() {
+		return nil, base.ErrNotFile
+	}
+	var resp DownloadResp
+	params := map[string]string{
+		"method": "filemetas",
+		"fsids":  fmt.Sprintf("[%s]", file.Id),
+		"dlink":  "1",
+	}
+	_, err = driver.Get("/xpan/multimedia", params, &resp, account)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("%s&access_token=%s", resp.List[0].Dlink, account.AccessToken)
+	res, err := base.NoRedirectClient.R().SetHeader("User-Agent", "pan.baidu.com").Head(u)
+	if err != nil {
+		return nil, err
+	}
+	//if res.StatusCode() == 302 {
+	u = res.Header().Get("location")
+	//}
+	return &base.Link{
+		Url: u,
+		Headers: []base.Header{
+			{Name: "User-Agent", Value: "pan.baidu.com"},
+		}}, nil
+}
+
+func (driver Baidu) Path(path string, account *model.Account) (*model.File, []model.File, error) {
+	file, err := driver.File(path, account)
+	if err != nil {
+		return nil, nil, err
+	}
+	if !file.IsDir() {
+		return file, nil, nil
+	}
+	files, err := driver.Files(path, account)
+	if err != nil {
+		return nil, nil, err
+	}
+	return nil, files, nil
+}
+
+//func (driver Baidu) Proxy(r *http.Request, account *model.Account) {
+//	r.Header.Set("User-Agent", "pan.baidu.com")
+//}
+
+func (driver Baidu) Preview(path string, account *model.Account) (interface{}, error) {
+	return nil, base.ErrNotSupport
+}
+
+func (driver Baidu) MakeDir(path string, account *model.Account) error {
+	_, err := driver.create(utils.Join(account.RootFolder, path), 0, 1, "", "", account)
+	return err
+}
+
+func (driver Baidu) Move(src string, dst string, account *model.Account) error {
+	path := utils.Join(account.RootFolder, src)
+	dest, newname := utils.Split(utils.Join(account.RootFolder, dst))
+	data := []base.Json{
+		{
+			"path":    path,
+			"dest":    dest,
+			"newname": newname,
+		},
+	}
+	_, err := driver.manage("move", data, account)
+	return err
+}
+
+func (driver Baidu) Rename(src string, dst string, account *model.Account) error {
+	path := utils.Join(account.RootFolder, src)
+	newname := utils.Base(dst)
+	data := []base.Json{
+		{
+			"path":    path,
+			"newname": newname,
+		},
+	}
+	_, err := driver.manage("rename", data, account)
+	return err
+}
+
+func (driver Baidu) Copy(src string, dst string, account *model.Account) error {
+	path := utils.Join(account.RootFolder, src)
+	dest, newname := utils.Split(utils.Join(account.RootFolder, dst))
+	data := []base.Json{
+		{
+			"path":    path,
+			"dest":    dest,
+			"newname": newname,
+		},
+	}
+	_, err := driver.manage("copy", data, account)
+	return err
+}
+
+func (driver Baidu) Delete(path string, account *model.Account) error {
+	path = utils.Join(account.RootFolder, path)
+	data := []string{path}
+	_, err := driver.manage("delete", data, account)
+	return err
+}
+
+func (driver Baidu) Upload(file *model.FileStream, account *model.Account) error {
+	if file == nil {
+		return base.ErrEmptyFile
+	}
+	tempFile, err := ioutil.TempFile("data/temp", "file-*")
+	if err != nil {
+		return err
+	}
+	defer func() {
+		_ = tempFile.Close()
+		_ = os.Remove(tempFile.Name())
+	}()
+	_, err = io.Copy(tempFile, file)
+	if err != nil {
+		return err
+	}
+	_, err = tempFile.Seek(0, io.SeekStart)
+	if err != nil {
+		return err
+	}
+	var Default uint64 = 4 * 1024 * 1024
+	defaultByteData := make([]byte, Default)
+	count := int(math.Ceil(float64(file.GetSize()) / float64(Default)))
+	var SliceSize uint64 = 256 * 1024
+	// cal md5
+	h1 := md5.New()
+	h2 := md5.New()
+	block_list := make([]string, 0)
+	content_md5 := ""
+	slice_md5 := ""
+	left := file.GetSize()
+	for i := 0; i < count; i++ {
+		byteSize := Default
+		var byteData []byte
+		if left < Default {
+			byteSize = left
+			byteData = make([]byte, byteSize)
+		} else {
+			byteData = defaultByteData
+		}
+		left -= byteSize
+		_, err = io.ReadFull(tempFile, byteData)
+		if err != nil {
+			return err
+		}
+		h1.Write(byteData)
+		h2.Write(byteData)
+		block_list = append(block_list, fmt.Sprintf("\"%s\"", hex.EncodeToString(h2.Sum(nil))))
+		h2.Reset()
+	}
+	content_md5 = hex.EncodeToString(h1.Sum(nil))
+	_, err = tempFile.Seek(0, io.SeekStart)
+	if err != nil {
+		return err
+	}
+	if file.GetSize() <= SliceSize {
+		slice_md5 = content_md5
+	} else {
+		sliceData := make([]byte, SliceSize)
+		_, err = io.ReadFull(tempFile, sliceData)
+		if err != nil {
+			return err
+		}
+		h2.Write(sliceData)
+		slice_md5 = hex.EncodeToString(h2.Sum(nil))
+		_, err = tempFile.Seek(0, io.SeekStart)
+		if err != nil {
+			return err
+		}
+	}
+	path := encodeURIComponent(utils.Join(account.RootFolder, file.ParentPath, file.Name))
+	block_list_str := fmt.Sprintf("[%s]", strings.Join(block_list, ","))
+	data := fmt.Sprintf("path=%s&size=%d&isdir=0&autoinit=1&block_list=%s&content-md5=%s&slice-md5=%s",
+		path, file.GetSize(),
+		block_list_str,
+		content_md5, slice_md5)
+	params := map[string]string{
+		"method": "precreate",
+	}
+	var precreateResp PrecreateResp
+	_, err = driver.Post("/xpan/file", params, data, &precreateResp, account)
+	if err != nil {
+		return err
+	}
+	log.Debugf("%+v", precreateResp)
+	if precreateResp.ReturnType == 2 {
+		return nil
+	}
+	params = map[string]string{
+		"method":       "upload",
+		"access_token": account.AccessToken,
+		"type":         "tmpfile",
+		"path":         path,
+		"uploadid":     precreateResp.Uploadid,
+	}
+	left = file.GetSize()
+	for _, partseq := range precreateResp.BlockList {
+		byteSize := Default
+		var byteData []byte
+		if left < Default {
+			byteSize = left
+			byteData = make([]byte, byteSize)
+		} else {
+			byteData = defaultByteData
+		}
+		left -= byteSize
+		_, err = io.ReadFull(tempFile, byteData)
+		if err != nil {
+			return err
+		}
+		u := "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2"
+		params["partseq"] = strconv.Itoa(partseq)
+		res, err := base.RestyClient.R().SetQueryParams(params).SetFileReader("file", file.Name, bytes.NewReader(byteData)).Post(u)
+		if err != nil {
+			return err
+		}
+		log.Debugln(res.String())
+	}
+	_, err = driver.create(path, file.GetSize(), 0, precreateResp.Uploadid, block_list_str, account)
+	return err
+}
+
+var _ base.Driver = (*Baidu)(nil)
diff --git a/drivers/baidu/types.go b/drivers/baidu/types.go
new file mode 100644
index 00000000000..36a2105336c
--- /dev/null
+++ b/drivers/baidu/types.go
@@ -0,0 +1,84 @@
+package baidu
+
+type TokenErrResp struct {
+	ErrorDescription string `json:"error_description"`
+	Error            string `json:"error"`
+}
+
+type File struct {
+	//TkbindId     int    `json:"tkbind_id"`
+	//OwnerType    int    `json:"owner_type"`
+	//Category     int    `json:"category"`
+	//RealCategory string `json:"real_category"`
+	FsId        int64 `json:"fs_id"`
+	ServerMtime int64 `json:"server_mtime"`
+	//OperId      int   `json:"oper_id"`
+	//ServerCtime int   `json:"server_ctime"`
+	Thumbs struct {
+		//Icon string `json:"icon"`
+		Url3 string `json:"url3"`
+		//Url2 string `json:"url2"`
+		//Url1 string `json:"url1"`
+	} `json:"thumbs"`
+	//Wpfile         int    `json:"wpfile"`
+	//LocalMtime     int    `json:"local_mtime"`
+	Size int64 `json:"size"`
+	//ExtentTinyint7 int    `json:"extent_tinyint7"`
+	Path string `json:"path"`
+	//Share          int    `json:"share"`
+	//ServerAtime    int    `json:"server_atime"`
+	//Pl             int    `json:"pl"`
+	//LocalCtime     int    `json:"local_ctime"`
+	ServerFilename string `json:"server_filename"`
+	//Md5            string `json:"md5"`
+	//OwnerId        int    `json:"owner_id"`
+	//Unlist int `json:"unlist"`
+	Isdir int `json:"isdir"`
+}
+
+type ListResp struct {
+	Errno     int    `json:"errno"`
+	GuidInfo  string `json:"guid_info"`
+	List      []File `json:"list"`
+	RequestId int64  `json:"request_id"`
+	Guid      int    `json:"guid"`
+}
+
+type DownloadResp struct {
+	Errmsg string `json:"errmsg"`
+	Errno  int    `json:"errno"`
+	List   []struct {
+		//Category    int    `json:"category"`
+		//DateTaken   int    `json:"date_taken,omitempty"`
+		Dlink string `json:"dlink"`
+		//Filename    string `json:"filename"`
+		//FsId        int64  `json:"fs_id"`
+		//Height      int    `json:"height,omitempty"`
+		//Isdir       int    `json:"isdir"`
+		//Md5         string `json:"md5"`
+		//OperId      int    `json:"oper_id"`
+		//Path        string `json:"path"`
+		//ServerCtime int    `json:"server_ctime"`
+		//ServerMtime int    `json:"server_mtime"`
+		//Size        int    `json:"size"`
+		//Thumbs      struct {
+		//	Icon string `json:"icon,omitempty"`
+		//	Url1 string `json:"url1,omitempty"`
+		//	Url2 string `json:"url2,omitempty"`
+		//	Url3 string `json:"url3,omitempty"`
+		//} `json:"thumbs"`
+		//Width int `json:"width,omitempty"`
+	} `json:"list"`
+	//Names struct {
+	//} `json:"names"`
+	RequestId string `json:"request_id"`
+}
+
+type PrecreateResp struct {
+	Path       string `json:"path"`
+	Uploadid   string `json:"uploadid"`
+	ReturnType int    `json:"return_type"`
+	BlockList  []int  `json:"block_list"`
+	Errno      int    `json:"errno"`
+	RequestId  int64  `json:"request_id"`
+}
diff --git a/drivers/baidu/util.go b/drivers/baidu/util.go
new file mode 100644
index 00000000000..bf41c7f13b0
--- /dev/null
+++ b/drivers/baidu/util.go
@@ -0,0 +1,18 @@
+package baidu
+
+import (
+	"net/url"
+	"strings"
+	"time"
+)
+
+func getTime(t int64) *time.Time {
+	tm := time.Unix(t, 0)
+	return &tm
+}
+
+func encodeURIComponent(str string) string {
+	r := url.QueryEscape(str)
+	r = strings.ReplaceAll(r, "+", "%20")
+	return r
+}
diff --git a/drivers/base/driver.go b/drivers/base/driver.go
index d5b7e4b4a4c..8e74282c1e2 100644
--- a/drivers/base/driver.go
+++ b/drivers/base/driver.go
@@ -2,10 +2,10 @@ package base
 
 import (
 	"github.com/Xhofe/alist/model"
-	"github.com/gin-gonic/gin"
 	"github.com/go-resty/resty/v2"
 	log "github.com/sirupsen/logrus"
 	"net/http"
+	"time"
 )
 
 type DriverConfig struct {
@@ -38,8 +38,8 @@ type Driver interface {
 	Link(args Args, account *model.Account) (*Link, error)
 	// Path 取路径（文件或文件夹）
 	Path(path string, account *model.Account) (*model.File, []model.File, error)
-	// Proxy 代理处理
-	Proxy(c *gin.Context, account *model.Account)
+	// Deprecated Proxy 代理处理
+	//Proxy(r *http.Request, account *model.Account)
 	// Preview 预览
 	Preview(path string, account *model.Account) (interface{}, error)
 	// MakeDir 创建文件夹
@@ -62,6 +62,7 @@ type Item struct {
 	Name        string `json:"name"`
 	Label       string `json:"label"`
 	Type        string `json:"type"`
+	Default     string `json:"default"`
 	Values      string `json:"values"`
 	Required    bool   `json:"required"`
 	Description string `json:"description"`
@@ -84,7 +85,7 @@ func GetDriversMap() map[string]Driver {
 }
 
 func GetDrivers() map[string][]Item {
-	res := make(map[string][]Item, 0)
+	res := make(map[string][]Item)
 	for k, v := range driversMap {
 		if v.Config().OnlyProxy {
 			res[k] = v.Items()
@@ -111,6 +112,13 @@ func GetDrivers() map[string][]Item {
 					Required:    true,
 					Description: "Transfer the WebDAV of this account through the server",
 				},
+				{
+					Name:        "webdav_direct",
+					Label:       "webdav direct",
+					Type:        TypeBool,
+					Required:    true,
+					Description: "Transfer the WebDAV of this account through the native",
+				},
 			}, v.Items()...)
 		}
 		res[k] = append([]Item{
@@ -160,6 +168,8 @@ func GetDrivers() map[string][]Item {
 var NoRedirectClient *resty.Client
 var RestyClient = resty.New()
 var HttpClient = &http.Client{}
+var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
+var DefaultTimeout = time.Second * 20
 
 func init() {
 	NoRedirectClient = resty.New().SetRedirectPolicy(
@@ -167,8 +177,8 @@ func init() {
 			return http.ErrUseLastResponse
 		}),
 	)
-	userAgent := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
-	NoRedirectClient.SetHeader("user-agent", userAgent)
-	RestyClient.SetHeader("user-agent", userAgent)
+	NoRedirectClient.SetHeader("user-agent", UserAgent)
+	RestyClient.SetHeader("user-agent", UserAgent)
 	RestyClient.SetRetryCount(3)
+	RestyClient.SetTimeout(DefaultTimeout)
 }
diff --git a/drivers/base/types.go b/drivers/base/types.go
index 233482623b4..5456b80008e 100644
--- a/drivers/base/types.go
+++ b/drivers/base/types.go
@@ -13,6 +13,7 @@ var (
 	ErrNotFolder    = errors.New("not a folder")
 	ErrEmptyFile    = errors.New("empty file")
 	ErrRelativePath = errors.New("access using relative path is not allowed")
+	ErrEmptyToken   = errors.New("empty token")
 )
 
 const (
@@ -43,7 +44,8 @@ type Header struct {
 }
 
 type Link struct {
-	Url     string   `json:"url"`
-	Headers []Header `json:"headers"`
-	Data    io.ReadCloser
+	Url      string   `json:"url"`
+	Headers  []Header `json:"headers"`
+	Data     io.ReadCloser
+	FilePath string `json:"path"` // for native
 }
diff --git a/drivers/ftp/driver.go b/drivers/ftp/driver.go
index 6132c8a48ca..d5915a95813 100644
--- a/drivers/ftp/driver.go
+++ b/drivers/ftp/driver.go
@@ -5,7 +5,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	"github.com/jlaffaye/ftp"
 	log "github.com/sirupsen/logrus"
 	"path/filepath"
@@ -186,9 +185,9 @@ func (driver FTP) Path(path string, account *model.Account) (*model.File, []mode
 	return nil, files, nil
 }
 
-func (driver FTP) Proxy(c *gin.Context, account *model.Account) {
-
-}
+//func (driver FTP) Proxy(r *http.Request, account *model.Account) {
+//
+//}
 
 func (driver FTP) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/ftp/ftp.go b/drivers/ftp/ftp.go
index cc2995bcbbf..e3f7353badc 100644
--- a/drivers/ftp/ftp.go
+++ b/drivers/ftp/ftp.go
@@ -11,7 +11,12 @@ var connMap map[string]*ftp.ServerConn
 func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) {
 	conn, ok := connMap[account.Name]
 	if ok {
-		return conn, nil
+		_, err := conn.CurrentDir()
+		if err == nil {
+			return conn, nil
+		} else {
+			delete(connMap, account.Name)
+		}
 	}
 	conn, err := ftp.Connect(account.SiteUrl)
 	if err != nil {
@@ -21,9 +26,11 @@ func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) {
 	if err != nil {
 		return nil, err
 	}
+	connMap[account.Name] = conn
 	return conn, nil
 }
 
 func init() {
 	base.RegisterDriver(&FTP{})
+	connMap = make(map[string]*ftp.ServerConn)
 }
diff --git a/drivers/google/driver.go b/drivers/google/driver.go
index af64ebb8187..eda794b5f51 100644
--- a/drivers/google/driver.go
+++ b/drivers/google/driver.go
@@ -6,7 +6,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	log "github.com/sirupsen/logrus"
 	"io/ioutil"
 	"path/filepath"
@@ -178,9 +177,9 @@ func (driver GoogleDrive) Path(path string, account *model.Account) (*model.File
 	return nil, files, nil
 }
 
-func (driver GoogleDrive) Proxy(c *gin.Context, account *model.Account) {
-	c.Request.Header.Add("Authorization", "Bearer "+account.AccessToken)
-}
+//func (driver GoogleDrive) Proxy(r *http.Request, account *model.Account) {
+//	r.Header.Add("Authorization", "Bearer "+account.AccessToken)
+//}
 
 func (driver GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/lanzou/driver.go b/drivers/lanzou/driver.go
index 1636324424a..ca719a41462 100644
--- a/drivers/lanzou/driver.go
+++ b/drivers/lanzou/driver.go
@@ -5,7 +5,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	log "github.com/sirupsen/logrus"
 	"path/filepath"
 )
@@ -138,6 +137,9 @@ func (driver Lanzou) Link(args base.Args, account *model.Account) (*base.Link, e
 	}
 	link := base.Link{
 		Url: url,
+		Headers: []base.Header{
+			{Name: "User-Agent", Value: base.UserAgent},
+		},
 	}
 	return &link, nil
 }
@@ -159,9 +161,9 @@ func (driver Lanzou) Path(path string, account *model.Account) (*model.File, []m
 	return nil, files, nil
 }
 
-func (driver Lanzou) Proxy(c *gin.Context, account *model.Account) {
-	c.Request.Header.Del("Origin")
-}
+//func (driver Lanzou) Proxy(r *http.Request, account *model.Account) {
+//	r.Header.Del("Origin")
+//}
 
 func (driver Lanzou) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/lanzou/lanzou.go b/drivers/lanzou/lanzou.go
index d9556d499af..78ff94b5779 100644
--- a/drivers/lanzou/lanzou.go
+++ b/drivers/lanzou/lanzou.go
@@ -6,7 +6,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/go-resty/resty/v2"
 	log "github.com/sirupsen/logrus"
 	"net/url"
 	"path/filepath"
@@ -15,8 +14,6 @@ import (
 	"time"
 )
 
-var lanzouClient = resty.New()
-
 type LanZouFile struct {
 	Name    string `json:"name"`
 	NameAll string `json:"name_all"`
@@ -58,7 +55,7 @@ func (driver *Lanzou) GetFiles(folderId string, account *model.Account) ([]LanZo
 		files := make([]LanZouFile, 0)
 		var resp LanZouFilesResp
 		// folders
-		res, err := lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
+		res, err := base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
 			SetFormData(map[string]string{
 				"task":      "47",
 				"folder_id": folderId,
@@ -77,7 +74,7 @@ func (driver *Lanzou) GetFiles(folderId string, account *model.Account) ([]LanZo
 		// files
 		pg := 1
 		for {
-			_, err = lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
+			_, err = base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
 				SetFormData(map[string]string{
 					"task":      "5",
 					"folder_id": folderId,
@@ -108,7 +105,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
 	if err != nil {
 		return nil, err
 	}
-	res, err := lanzouClient.R().Get(shareUrl)
+	res, err := base.RestyClient.R().Get(shareUrl)
 	if err != nil {
 		return nil, err
 	}
@@ -129,7 +126,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
 	pg := 1
 	for {
 		var resp LanZouFilesResp
-		res, err = lanzouClient.R().SetResult(&resp).SetFormData(map[string]string{
+		res, err = base.RestyClient.R().SetResult(&resp).SetFormData(map[string]string{
 			"lx":  lx,
 			"fid": fid,
 			"uid": uid,
@@ -166,7 +163,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
 // GetDownPageId 获取下载页面的ID
 func (driver *Lanzou) GetDownPageId(fileId string, account *model.Account) (string, error) {
 	var resp LanZouFilesResp
-	res, err := lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
+	res, err := base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
 		SetFormData(map[string]string{
 			"task":    "22",
 			"file_id": fileId,
@@ -201,7 +198,7 @@ func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, er
 	if err != nil {
 		return "", err
 	}
-	res, err := lanzouClient.R().Get(fmt.Sprintf("https://%s/%s", u.Host, downId))
+	res, err := base.RestyClient.R().Get(fmt.Sprintf("https://%s/%s", u.Host, downId))
 	if err != nil {
 		return "", err
 	}
@@ -209,19 +206,24 @@ func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, er
 	if len(iframe) == 0 {
 		return "", fmt.Errorf("get down empty page")
 	}
-	iframeUrl := "https://wwa.lanzouo.com" + iframe[1]
-	res, err = lanzouClient.R().Get(iframeUrl)
+	iframeUrl := fmt.Sprintf("https://%s%s", u.Host, iframe[1])
+	res, err = base.RestyClient.R().Get(iframeUrl)
 	if err != nil {
 		return "", err
 	}
+	log.Debugln(res.String())
 	ajaxdata := regexp.MustCompile(`var ajaxdata = '(.+?)'`).FindStringSubmatch(res.String())
 	if len(ajaxdata) == 0 {
 		return "", fmt.Errorf("get iframe empty page")
 	}
 	signs := ajaxdata[1]
-	sign := regexp.MustCompile(`var ispostdowns = '(.+?)';`).FindStringSubmatch(res.String())[1]
-	websign := regexp.MustCompile(`'websign':'(.+?)'`).FindStringSubmatch(res.String())[1]
-	websignkey := regexp.MustCompile(`'websignkey':'(.+?)'`).FindStringSubmatch(res.String())[1]
+	//sign := regexp.MustCompile(`var ispostdowns = '(.+?)';`).FindStringSubmatch(res.String())[1]
+	sign := regexp.MustCompile(`'sign':'(.+?)',`).FindStringSubmatch(res.String())[1]
+	//websign := regexp.MustCompile(`'websign':'(.+?)'`).FindStringSubmatch(res.String())[1]
+	//websign := regexp.MustCompile(`var websign = '(.+?)'`).FindStringSubmatch(res.String())[1]
+	websign := ""
+	//websignkey := regexp.MustCompile(`'websignkey':'(.+?)'`).FindStringSubmatch(res.String())[1]
+	websignkey := regexp.MustCompile(`var websignkey = '(.+?)';`).FindStringSubmatch(res.String())[1]
 	var resp LanzouLinkResp
 	form := map[string]string{
 		"action":     "downprocess",
@@ -232,7 +234,7 @@ func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, er
 		"websignkey": websignkey,
 	}
 	log.Debugf("form: %+v", form)
-	res, err = lanzouClient.R().SetResult(&resp).
+	res, err = base.RestyClient.R().SetResult(&resp).
 		SetHeader("origin", "https://"+u.Host).
 		SetHeader("referer", iframeUrl).
 		SetFormData(form).Post(fmt.Sprintf("https://%s/ajaxm.php", u.Host))
@@ -248,7 +250,4 @@ func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, er
 
 func init() {
 	base.RegisterDriver(&Lanzou{})
-	lanzouClient.
-		SetRetryCount(3).
-		SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
 }
diff --git a/drivers/mediatrack/driver.go b/drivers/mediatrack/driver.go
index 8f3245db89a..9e3ab0ab471 100644
--- a/drivers/mediatrack/driver.go
+++ b/drivers/mediatrack/driver.go
@@ -12,7 +12,6 @@ import (
 	"github.com/aws/aws-sdk-go/aws/credentials"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/s3/s3manager"
-	"github.com/gin-gonic/gin"
 	"github.com/google/uuid"
 	jsoniter "github.com/json-iterator/go"
 	log "github.com/sirupsen/logrus"
@@ -46,11 +45,12 @@ func (driver MediaTrack) Items() []base.Item {
 			Required: true,
 		},
 		{
-			Name:     "order_by",
-			Label:    "order_by",
-			Type:     base.TypeSelect,
-			Values:   "updated_at,title,size",
-			Required: true,
+			Name:        "order_by",
+			Label:       "order_by",
+			Type:        base.TypeSelect,
+			Values:      "updated_at,title,size",
+			Required:    true,
+			Description: "title",
 		},
 		{
 			Name:     "order_direction",
@@ -58,6 +58,7 @@ func (driver MediaTrack) Items() []base.Item {
 			Type:     base.TypeSelect,
 			Values:   "true,false",
 			Required: true,
+			Default:  "false",
 		},
 	}
 }
@@ -148,9 +149,9 @@ func (driver MediaTrack) Path(path string, account *model.Account) (*model.File,
 	return nil, files, nil
 }
 
-func (driver MediaTrack) Proxy(c *gin.Context, account *model.Account) {
-
-}
+//func (driver MediaTrack) Proxy(r *http.Request, account *model.Account) {
+//
+//}
 
 func (driver MediaTrack) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotImplement
diff --git a/drivers/native/driver.go b/drivers/native/driver.go
index 572bbbf9a64..36b1573ce6e 100644
--- a/drivers/native/driver.go
+++ b/drivers/native/driver.go
@@ -6,7 +6,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"io/ioutil"
@@ -133,7 +132,7 @@ func (driver Native) Link(args base.Args, account *model.Account) (*base.Link, e
 		return nil, base.ErrNotFile
 	}
 	link := base.Link{
-		Url: fullPath,
+		FilePath: fullPath,
 	}
 	return &link, nil
 }
@@ -156,9 +155,9 @@ func (driver Native) Path(path string, account *model.Account) (*model.File, []m
 	return nil, files, nil
 }
 
-func (driver Native) Proxy(c *gin.Context, account *model.Account) {
-	// unnecessary
-}
+//func (driver Native) Proxy(r *http.Request, account *model.Account) {
+//	// unnecessary
+//}
 
 func (driver Native) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/onedrive/driver.go b/drivers/onedrive/driver.go
index 6355c493abb..e9e3e84c54f 100644
--- a/drivers/onedrive/driver.go
+++ b/drivers/onedrive/driver.go
@@ -6,8 +6,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
-	"github.com/robfig/cron/v3"
 	log "github.com/sirupsen/logrus"
 	"path/filepath"
 )
@@ -92,9 +90,9 @@ func (driver Onedrive) Items() []base.Item {
 }
 
 func (driver Onedrive) Save(account *model.Account, old *model.Account) error {
-	if old != nil {
-		conf.Cron.Remove(cron.EntryID(old.CronId))
-	}
+	//if old != nil {
+	//	conf.Cron.Remove(cron.EntryID(old.CronId))
+	//}
 	if account == nil {
 		return nil
 	}
@@ -104,28 +102,25 @@ func (driver Onedrive) Save(account *model.Account, old *model.Account) error {
 	}
 	account.RootFolder = utils.ParsePath(account.RootFolder)
 	err := driver.RefreshToken(account)
+	_ = model.SaveAccount(account)
 	if err != nil {
 		return err
 	}
-	cronId, err := conf.Cron.AddFunc("@every 1h", func() {
-		name := account.Name
-		log.Debugf("onedrive account name: %s", name)
-		newAccount, ok := model.GetAccount(name)
-		log.Debugf("onedrive account: %+v", newAccount)
-		if !ok {
-			return
-		}
-		err = driver.RefreshToken(&newAccount)
-		_ = model.SaveAccount(&newAccount)
-	})
-	if err != nil {
-		return err
-	}
-	account.CronId = int(cronId)
-	err = model.SaveAccount(account)
-	if err != nil {
-		return err
-	}
+	//cronId, err := conf.Cron.AddFunc("@every 1h", func() {
+	//	name := account.Name
+	//	log.Debugf("onedrive account name: %s", name)
+	//	newAccount, ok := model.GetAccount(name)
+	//	log.Debugf("onedrive account: %+v", newAccount)
+	//	if !ok {
+	//		return
+	//	}
+	//	err = driver.RefreshToken(&newAccount)
+	//	_ = model.SaveAccount(&newAccount)
+	//})
+	//if err != nil {
+	//	return err
+	//}
+	//account.CronId = int(cronId)
 	return nil
 }
 
@@ -205,9 +200,9 @@ func (driver Onedrive) Path(path string, account *model.Account) (*model.File, [
 	return nil, files, nil
 }
 
-func (driver Onedrive) Proxy(c *gin.Context, account *model.Account) {
-	c.Request.Header.Del("Origin")
-}
+//func (driver Onedrive) Proxy(r *http.Request, account *model.Account) {
+//	r.Header.Del("Origin")
+//}
 
 func (driver Onedrive) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/onedrive/onedrive.go b/drivers/onedrive/onedrive.go
index 9967b950abb..88e1c497590 100644
--- a/drivers/onedrive/onedrive.go
+++ b/drivers/onedrive/onedrive.go
@@ -19,8 +19,6 @@ import (
 	"time"
 )
 
-var oneClient = resty.New()
-
 type Host struct {
 	Oauth string
 	Api   string
@@ -47,7 +45,7 @@ var onedriveHostMap = map[string]Host{
 
 func (driver Onedrive) GetMetaUrl(account *model.Account, auth bool, path string) string {
 	path = filepath.Join(account.RootFolder, path)
-	log.Debugf(path)
+	//log.Debugf(path)
 	host, _ := onedriveHostMap[account.Zone]
 	if auth {
 		return host.Oauth
@@ -81,7 +79,7 @@ type OneTokenErr struct {
 
 func (driver Onedrive) RefreshToken(account *model.Account) error {
 	err := driver.refreshToken(account)
-	if err != nil && err.Error() == "empty refresh_token" {
+	if err != nil && err == base.ErrEmptyToken {
 		return driver.refreshToken(account)
 	}
 	return err
@@ -91,7 +89,7 @@ func (driver Onedrive) refreshToken(account *model.Account) error {
 	url := driver.GetMetaUrl(account, true, "") + "/common/oauth2/v2.0/token"
 	var resp base.TokenResp
 	var e OneTokenErr
-	_, err := oneClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
+	_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
 		"grant_type":    "refresh_token",
 		"client_id":     account.ClientId,
 		"client_secret": account.ClientSecret,
@@ -109,8 +107,8 @@ func (driver Onedrive) refreshToken(account *model.Account) error {
 		account.Status = "work"
 	}
 	if resp.RefreshToken == "" {
-		account.Status = "empty refresh_token"
-		return errors.New("empty refresh_token")
+		account.Status = base.ErrEmptyToken.Error()
+		return base.ErrEmptyToken
 	}
 	account.RefreshToken, account.AccessToken = resp.RefreshToken, resp.AccessToken
 	return nil
@@ -178,16 +176,17 @@ func (driver Onedrive) GetFiles(account *model.Account, path string) ([]OneFile,
 	}
 	for nextLink != "" {
 		var files OneFiles
-		var e OneRespErr
-		_, err := oneClient.R().SetResult(&files).SetError(&e).
-			SetHeader("Authorization", "Bearer  "+account.AccessToken).
-			Get(nextLink)
+		_, err := driver.Request(nextLink, base.Get, nil, nil, nil, nil, &files, account)
+		//var e OneRespErr
+		//_, err := oneClient.R().SetResult(&files).SetError(&e).
+		//	SetHeader("Authorization", "Bearer  "+account.AccessToken).
+		//	Get(nextLink)
 		if err != nil {
 			return nil, err
 		}
-		if e.Error.Code != "" {
-			return nil, fmt.Errorf("%s", e.Error.Message)
-		}
+		//if e.Error.Code != "" {
+		//	return nil, fmt.Errorf("%s", e.Error.Message)
+		//}
 		res = append(res, files.Value...)
 		nextLink = files.NextLink
 	}
@@ -196,16 +195,18 @@ func (driver Onedrive) GetFiles(account *model.Account, path string) ([]OneFile,
 
 func (driver Onedrive) GetFile(account *model.Account, path string) (*OneFile, error) {
 	var file OneFile
-	var e OneRespErr
-	_, err := oneClient.R().SetResult(&file).SetError(&e).
-		SetHeader("Authorization", "Bearer  "+account.AccessToken).
-		Get(driver.GetMetaUrl(account, false, path))
+	//var e OneRespErr
+	u := driver.GetMetaUrl(account, false, path)
+	_, err := driver.Request(u, base.Get, nil, nil, nil, nil, &file, account)
+	//_, err := oneClient.R().SetResult(&file).SetError(&e).
+	//	SetHeader("Authorization", "Bearer  "+account.AccessToken).
+	//	Get(driver.GetMetaUrl(account, false, path))
 	if err != nil {
 		return nil, err
 	}
-	if e.Error.Code != "" {
-		return nil, fmt.Errorf("%s", e.Error.Message)
-	}
+	//if e.Error.Code != "" {
+	//	return nil, fmt.Errorf("%s", e.Error.Message)
+	//}
 	return &file, nil
 }
 
@@ -252,7 +253,7 @@ func (driver Onedrive) Request(url string, method int, headers, query, form map[
 	if err != nil {
 		return nil, err
 	}
-	log.Debug(res.String())
+	//log.Debug(res.String())
 	if e.Error.Code != "" {
 		if e.Error.Code == "InvalidAuthenticationToken" {
 			err = driver.RefreshToken(account)
@@ -314,5 +315,4 @@ func (driver Onedrive) UploadBig(file *model.FileStream, account *model.Account)
 
 func init() {
 	base.RegisterDriver(&Onedrive{})
-	oneClient.SetRetryCount(3)
 }
diff --git a/drivers/operate/operate.go b/drivers/operate/operate.go
index 2dd042cc080..fef8b2015b6 100644
--- a/drivers/operate/operate.go
+++ b/drivers/operate/operate.go
@@ -24,14 +24,7 @@ func Files(driver base.Driver, account *model.Account, path string) ([]model.Fil
 }
 
 func File(driver base.Driver, account *model.Account, path string) (*model.File, error) {
-	file, _, err := Path(driver, account, path)
-	if err != nil {
-		return nil, err
-	}
-	if file == nil {
-		return nil, base.ErrNotFolder
-	}
-	return file, nil
+	return driver.File(path, account)
 }
 
 func MakeDir(driver base.Driver, account *model.Account, path string, clearCache bool) error {
diff --git a/drivers/pikpak/driver.go b/drivers/pikpak/driver.go
index 73cb118abd1..46ddaed360d 100644
--- a/drivers/pikpak/driver.go
+++ b/drivers/pikpak/driver.go
@@ -10,7 +10,6 @@ import (
 	"github.com/aws/aws-sdk-go/aws/credentials"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/s3/s3manager"
-	"github.com/gin-gonic/gin"
 	jsoniter "github.com/json-iterator/go"
 	log "github.com/sirupsen/logrus"
 	"path/filepath"
@@ -142,9 +141,9 @@ func (driver PikPak) Path(path string, account *model.Account) (*model.File, []m
 	return nil, files, nil
 }
 
-func (driver PikPak) Proxy(c *gin.Context, account *model.Account) {
-
-}
+//func (driver PikPak) Proxy(r *http.Request, account *model.Account) {
+//
+//}
 
 func (driver PikPak) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/s3/driver.go b/drivers/s3/driver.go
index 7b005c065db..ef17fb98da3 100644
--- a/drivers/s3/driver.go
+++ b/drivers/s3/driver.go
@@ -8,7 +8,6 @@ import (
 	"github.com/Xhofe/alist/utils"
 	"github.com/aws/aws-sdk-go/service/s3"
 	"github.com/aws/aws-sdk-go/service/s3/s3manager"
-	"github.com/gin-gonic/gin"
 	log "github.com/sirupsen/logrus"
 	"net/url"
 	"path/filepath"
@@ -77,7 +76,7 @@ func (driver S3) Items() []base.Item {
 		{
 			Name:        "zone",
 			Label:       "placeholder filename",
-			Type:        base.TypeNumber,
+			Type:        base.TypeString,
 			Description: "default empty string",
 		},
 	}
@@ -142,7 +141,7 @@ func (driver S3) Files(path string, account *model.Account) ([]model.File, error
 }
 
 func (driver S3) Link(args base.Args, account *model.Account) (*base.Link, error) {
-	client, err := driver.GetClient(account)
+	client, err := driver.GetClient(account, true)
 	if err != nil {
 		return nil, err
 	}
@@ -189,9 +188,9 @@ func (driver S3) Path(path string, account *model.Account) (*model.File, []model
 	return nil, files, nil
 }
 
-func (driver S3) Proxy(c *gin.Context, account *model.Account) {
-
-}
+//func (driver S3) Proxy(r *http.Request, account *model.Account) {
+//
+//}
 
 func (driver S3) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
@@ -215,7 +214,7 @@ func (driver S3) Rename(src string, dst string, account *model.Account) error {
 }
 
 func (driver S3) Copy(src string, dst string, account *model.Account) error {
-	client, err := driver.GetClient(account)
+	client, err := driver.GetClient(account, false)
 	if err != nil {
 		return err
 	}
@@ -235,7 +234,7 @@ func (driver S3) Copy(src string, dst string, account *model.Account) error {
 }
 
 func (driver S3) Delete(path string, account *model.Account) error {
-	client, err := driver.GetClient(account)
+	client, err := driver.GetClient(account, false)
 	if err != nil {
 		return err
 	}
diff --git a/drivers/s3/s3.go b/drivers/s3/s3.go
index 05606d5521d..883ed3fbcf5 100644
--- a/drivers/s3/s3.go
+++ b/drivers/s3/s3.go
@@ -29,13 +29,13 @@ func (driver S3) NewSession(account *model.Account) (*session.Session, error) {
 	return session.NewSession(cfg)
 }
 
-func (driver S3) GetClient(account *model.Account) (*s3.S3, error) {
+func (driver S3) GetClient(account *model.Account, link bool) (*s3.S3, error) {
 	s, ok := sessionsMap[account.Name]
 	if !ok {
 		return nil, fmt.Errorf("can't find [%s] session", account.Name)
 	}
 	client := s3.New(s)
-	if account.CustomHost != "" {
+	if link && account.CustomHost != "" {
 		cURL, err := url.Parse(account.CustomHost)
 		if err != nil {
 			return nil, err
@@ -54,7 +54,7 @@ func (driver S3) GetClient(account *model.Account) (*s3.S3, error) {
 func (driver S3) List(prefix string, account *model.Account) ([]model.File, error) {
 	prefix = driver.GetKey(prefix, account, true)
 	log.Debugf("list: %s", prefix)
-	client, err := driver.GetClient(account)
+	client, err := driver.GetClient(account, false)
 	if err != nil {
 		return nil, err
 	}
@@ -118,6 +118,6 @@ func (driver S3) GetKey(path string, account *model.Account, dir bool) string {
 }
 
 func init() {
-	sessionsMap = make(map[string]*session.Session, 0)
+	sessionsMap = make(map[string]*session.Session)
 	base.RegisterDriver(&S3{})
 }
diff --git a/drivers/shandian/driver.go b/drivers/shandian/driver.go
index d3b66094ae4..506819a030d 100644
--- a/drivers/shandian/driver.go
+++ b/drivers/shandian/driver.go
@@ -6,7 +6,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	log "github.com/sirupsen/logrus"
 	"path/filepath"
 	"strconv"
@@ -154,9 +153,9 @@ func (driver Shandian) Path(path string, account *model.Account) (*model.File, [
 	return nil, files, nil
 }
 
-func (driver Shandian) Proxy(c *gin.Context, account *model.Account) {
-
-}
+//func (driver Shandian) Proxy(r *http.Request, account *model.Account) {
+//
+//}
 
 func (driver Shandian) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/teambition/driver.go b/drivers/teambition/driver.go
index a2189d240bd..17f9d9f085b 100644
--- a/drivers/teambition/driver.go
+++ b/drivers/teambition/driver.go
@@ -5,7 +5,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	log "github.com/sirupsen/logrus"
 	"path/filepath"
 )
@@ -52,6 +51,7 @@ func (driver Teambition) Items() []base.Item {
 			Type:     base.TypeSelect,
 			Values:   "fileName,fileSize,updated,created",
 			Required: true,
+			Default:  "fileName",
 		},
 		{
 			Name:     "order_direction",
@@ -59,6 +59,7 @@ func (driver Teambition) Items() []base.Item {
 			Type:     base.TypeSelect,
 			Values:   "Asc,Desc",
 			Required: true,
+			Default:  "Asc",
 		},
 	}
 }
@@ -149,9 +150,9 @@ func (driver Teambition) Path(path string, account *model.Account) (*model.File,
 	return nil, files, nil
 }
 
-func (driver Teambition) Proxy(c *gin.Context, account *model.Account) {
-
-}
+//func (driver Teambition) Proxy(r *http.Request, account *model.Account) {
+//
+//}
 
 func (driver Teambition) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/webdav/driver.go b/drivers/webdav/driver.go
index 0385f223c69..ee11c1ef152 100644
--- a/drivers/webdav/driver.go
+++ b/drivers/webdav/driver.go
@@ -5,7 +5,6 @@ import (
 	"github.com/Xhofe/alist/drivers/base"
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
-	"github.com/gin-gonic/gin"
 	"path/filepath"
 )
 
@@ -137,9 +136,9 @@ func (driver WebDav) Path(path string, account *model.Account) (*model.File, []m
 	return nil, files, nil
 }
 
-func (driver WebDav) Proxy(c *gin.Context, account *model.Account) {
-
-}
+//func (driver WebDav) Proxy(r *http.Request, account *model.Account) {
+//
+//}
 
 func (driver WebDav) Preview(path string, account *model.Account) (interface{}, error) {
 	return nil, base.ErrNotSupport
diff --git a/drivers/yandex/driver.go b/drivers/yandex/driver.go
new file mode 100644
index 00000000000..3957fec9491
--- /dev/null
+++ b/drivers/yandex/driver.go
@@ -0,0 +1,224 @@
+package yandex
+
+import (
+	"github.com/Xhofe/alist/conf"
+	"github.com/Xhofe/alist/drivers/base"
+	"github.com/Xhofe/alist/model"
+	"github.com/Xhofe/alist/utils"
+	log "github.com/sirupsen/logrus"
+	"net/http"
+	"path/filepath"
+	"strconv"
+)
+
+type Yandex struct{}
+
+func (driver Yandex) Config() base.DriverConfig {
+	return base.DriverConfig{
+		Name: "Yandex.Disk",
+	}
+}
+
+func (driver Yandex) Items() []base.Item {
+	return []base.Item{
+		{
+			Name:     "refresh_token",
+			Label:    "refresh token",
+			Type:     base.TypeString,
+			Required: true,
+		},
+		{
+			Name:     "root_folder",
+			Label:    "root folder path",
+			Type:     base.TypeString,
+			Default:  "/",
+			Required: true,
+		},
+		{
+			Name:     "order_by",
+			Label:    "order_by",
+			Type:     base.TypeSelect,
+			Default:  "name",
+			Values:   "name,path,created,modified,size",
+			Required: false,
+		},
+		{
+			Name:     "order_direction",
+			Label:    "order_direction",
+			Type:     base.TypeSelect,
+			Values:   "asc,desc",
+			Default:  "asc",
+			Required: false,
+		},
+		{
+			Name:     "client_id",
+			Label:    "client id",
+			Default:  "a78d5a69054042fa936f6c77f9a0ae8b",
+			Type:     base.TypeString,
+			Required: true,
+		},
+		{
+			Name:     "client_secret",
+			Label:    "client secret",
+			Default:  "9c119bbb04b346d2a52aa64401936b2b",
+			Type:     base.TypeString,
+			Required: true,
+		},
+	}
+}
+
+func (driver Yandex) Save(account *model.Account, old *model.Account) error {
+	return driver.RefreshToken(account)
+}
+
+func (driver Yandex) File(path string, account *model.Account) (*model.File, error) {
+	path = utils.ParsePath(path)
+	if path == "/" {
+		return &model.File{
+			Id:        account.RootFolder,
+			Name:      account.Name,
+			Size:      0,
+			Type:      conf.FOLDER,
+			Driver:    driver.Config().Name,
+			UpdatedAt: account.UpdatedAt,
+		}, nil
+	}
+	dir, name := filepath.Split(path)
+	files, err := driver.Files(dir, account)
+	if err != nil {
+		return nil, err
+	}
+	for _, file := range files {
+		if file.Name == name {
+			return &file, nil
+		}
+	}
+	return nil, base.ErrPathNotFound
+}
+
+func (driver Yandex) Files(path string, account *model.Account) ([]model.File, error) {
+	path = utils.ParsePath(path)
+	cache, err := base.GetCache(path, account)
+	if err == nil {
+		files, _ := cache.([]model.File)
+		return files, nil
+	}
+	files, err := driver.GetFiles(path, account)
+	if err != nil {
+		return nil, err
+	}
+	if len(files) > 0 {
+		_ = base.SetCache(path, files, account)
+	}
+	return files, nil
+}
+
+func (driver Yandex) Link(args base.Args, account *model.Account) (*base.Link, error) {
+	path := utils.Join(account.RootFolder, args.Path)
+	log.Debugln("down path:", path)
+	var resp DownResp
+	_, err := driver.Request("/download", base.Get, nil, map[string]string{
+		"path": path,
+	}, nil, nil, &resp, account)
+	if err != nil {
+		return nil, err
+	}
+	link := base.Link{
+		Url: resp.Href,
+	}
+	return &link, nil
+}
+
+func (driver Yandex) Path(path string, account *model.Account) (*model.File, []model.File, error) {
+	file, err := driver.File(path, account)
+	if err != nil {
+		return nil, nil, err
+	}
+	if !file.IsDir() {
+		return file, nil, nil
+	}
+	files, err := driver.Files(path, account)
+	if err != nil {
+		return nil, nil, err
+	}
+	return nil, files, nil
+}
+
+//func (driver Yandex) Proxy(r *http.Request, account *model.Account) {
+//
+//}
+
+func (driver Yandex) Preview(path string, account *model.Account) (interface{}, error) {
+	return nil, base.ErrNotSupport
+}
+
+func (driver Yandex) MakeDir(path string, account *model.Account) error {
+	path = utils.Join(account.RootFolder, path)
+	_, err := driver.Request("", base.Put, nil, map[string]string{
+		"path": path,
+	}, nil, nil, nil, account)
+	return err
+}
+
+func (driver Yandex) Move(src string, dst string, account *model.Account) error {
+	from := utils.Join(account.RootFolder, src)
+	path := utils.Join(account.RootFolder, dst)
+	_, err := driver.Request("/move", base.Post, nil, map[string]string{
+		"from":      from,
+		"path":      path,
+		"overwrite": "true",
+	}, nil, nil, nil, account)
+	return err
+}
+
+func (driver Yandex) Rename(src string, dst string, account *model.Account) error {
+	return driver.Move(src, dst, account)
+}
+
+func (driver Yandex) Copy(src string, dst string, account *model.Account) error {
+	from := utils.Join(account.RootFolder, src)
+	path := utils.Join(account.RootFolder, dst)
+	_, err := driver.Request("/copy", base.Post, nil, map[string]string{
+		"from":      from,
+		"path":      path,
+		"overwrite": "true",
+	}, nil, nil, nil, account)
+	return err
+}
+
+func (driver Yandex) Delete(path string, account *model.Account) error {
+	path = utils.Join(account.RootFolder, path)
+	_, err := driver.Request("", base.Delete, nil, map[string]string{
+		"path": path,
+	}, nil, nil, nil, account)
+	return err
+}
+
+func (driver Yandex) Upload(file *model.FileStream, account *model.Account) error {
+	if file == nil {
+		return base.ErrEmptyFile
+	}
+	path := utils.Join(account.RootFolder, file.ParentPath, file.Name)
+	var resp UploadResp
+	_, err := driver.Request("/upload", base.Get, nil, map[string]string{
+		"path":      path,
+		"overwrite": "true",
+	}, nil, nil, &resp, account)
+	if err != nil {
+		return err
+	}
+	req, err := http.NewRequest(resp.Method, resp.Href, file)
+	if err != nil {
+		return err
+	}
+	req.Header.Set("Content-Length", strconv.FormatUint(file.Size, 10))
+	req.Header.Set("Content-Type", "application/octet-stream")
+	_, err = base.HttpClient.Do(req)
+	//res, err := base.RestyClient.R().
+	//	SetHeader("Content-Length", strconv.FormatUint(file.Size, 10)).
+	//	SetBody(file).Put(resp.Href)
+	//log.Debugln(res.Status(), res.String())
+	return err
+}
+
+var _ base.Driver = (*Yandex)(nil)
diff --git a/drivers/yandex/types.go b/drivers/yandex/types.go
new file mode 100644
index 00000000000..9aed679dc1b
--- /dev/null
+++ b/drivers/yandex/types.go
@@ -0,0 +1,74 @@
+package yandex
+
+import "time"
+
+type TokenErrResp struct {
+	ErrorDescription string `json:"error_description"`
+	Error            string `json:"error"`
+}
+
+type ErrResp struct {
+	Message     string `json:"message"`
+	Description string `json:"description"`
+	Error       string `json:"error"`
+}
+
+type File struct {
+	//AntivirusStatus string `json:"antivirus_status"`
+	Size int64 `json:"size"`
+	//CommentIds      struct {
+	//	PrivateResource string `json:"private_resource"`
+	//	PublicResource  string `json:"public_resource"`
+	//} `json:"comment_ids"`
+	Name string `json:"name"`
+	//Exif struct {
+	//	DateTime time.Time `json:"date_time"`
+	//} `json:"exif"`
+	//Created    time.Time `json:"created"`
+	//ResourceId string    `json:"resource_id"`
+	Modified time.Time `json:"modified"`
+	//MimeType   string    `json:"mime_type"`
+	File string `json:"file"`
+	//MediaType  string    `json:"media_type"`
+	Preview string `json:"preview"`
+	Path    string `json:"path"`
+	//Sha256     string    `json:"sha256"`
+	Type string `json:"type"`
+	//Md5        string    `json:"md5"`
+	//Revision   int64     `json:"revision"`
+}
+
+type FilesResp struct {
+	Embedded struct {
+		Sort   string `json:"sort"`
+		Items  []File `json:"items"`
+		Limit  int    `json:"limit"`
+		Offset int    `json:"offset"`
+		Path   string `json:"path"`
+		Total  int    `json:"total"`
+	} `json:"_embedded"`
+	Name string `json:"name"`
+	Exif struct {
+	} `json:"exif"`
+	ResourceId string    `json:"resource_id"`
+	Created    time.Time `json:"created"`
+	Modified   time.Time `json:"modified"`
+	Path       string    `json:"path"`
+	CommentIds struct {
+	} `json:"comment_ids"`
+	Type     string `json:"type"`
+	Revision int64  `json:"revision"`
+}
+
+type DownResp struct {
+	Href      string `json:"href"`
+	Method    string `json:"method"`
+	Templated bool   `json:"templated"`
+}
+
+type UploadResp struct {
+	OperationId string `json:"operation_id"`
+	Href        string `json:"href"`
+	Method      string `json:"method"`
+	Templated   bool   `json:"templated"`
+}
diff --git a/drivers/yandex/util.go b/drivers/yandex/util.go
new file mode 100644
index 00000000000..cbe4e219ddc
--- /dev/null
+++ b/drivers/yandex/util.go
@@ -0,0 +1,1 @@
+package yandex
diff --git a/drivers/yandex/yandex.go b/drivers/yandex/yandex.go
new file mode 100644
index 00000000000..edb38672862
--- /dev/null
+++ b/drivers/yandex/yandex.go
@@ -0,0 +1,154 @@
+package yandex
+
+import (
+	"errors"
+	"fmt"
+	"github.com/Xhofe/alist/conf"
+	"github.com/Xhofe/alist/drivers/base"
+	"github.com/Xhofe/alist/model"
+	"github.com/Xhofe/alist/utils"
+	"github.com/go-resty/resty/v2"
+	"path"
+	"strconv"
+)
+
+func (driver Yandex) RefreshToken(account *model.Account) error {
+	err := driver.refreshToken(account)
+	if err != nil && err == base.ErrEmptyToken {
+		err = driver.refreshToken(account)
+	}
+	if err != nil {
+		account.Status = err.Error()
+	}
+	_ = model.SaveAccount(account)
+	return err
+}
+
+func (driver Yandex) refreshToken(account *model.Account) error {
+	u := "https://oauth.yandex.com/token"
+	var resp base.TokenResp
+	var e TokenErrResp
+	_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
+		"grant_type":    "refresh_token",
+		"refresh_token": account.RefreshToken,
+		"client_id":     account.ClientId,
+		"client_secret": account.ClientSecret,
+	}).Post(u)
+	if err != nil {
+		return err
+	}
+	if e.Error != "" {
+		return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
+	}
+	if resp.RefreshToken == "" {
+		return base.ErrEmptyToken
+	}
+	account.Status = "work"
+	account.AccessToken, account.RefreshToken = resp.AccessToken, resp.RefreshToken
+	return nil
+}
+
+func (driver Yandex) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
+	u := "https://cloud-api.yandex.net/v1/disk/resources" + pathname
+	req := base.RestyClient.R()
+	req.SetHeader("Authorization", "OAuth "+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 ErrResp
+	req.SetError(&e)
+	switch method {
+	case base.Get:
+		res, err = req.Get(u)
+	case base.Post:
+		res, err = req.Post(u)
+	case base.Patch:
+		res, err = req.Patch(u)
+	case base.Delete:
+		res, err = req.Delete(u)
+	case base.Put:
+		res, err = req.Put(u)
+	default:
+		return nil, base.ErrNotSupport
+	}
+	if err != nil {
+		return nil, err
+	}
+	//log.Debug(res.String())
+	if e.Error != "" {
+		if e.Error == "UnauthorizedError" {
+			err = driver.RefreshToken(account)
+			if err != nil {
+				return nil, err
+			}
+			return driver.Request(pathname, method, headers, query, form, data, resp, account)
+		}
+		return nil, errors.New(e.Description)
+	}
+	return res.Body(), nil
+}
+
+func (driver Yandex) GetFiles(rawPath string, account *model.Account) ([]model.File, error) {
+	path_ := utils.Join(account.RootFolder, rawPath)
+	limit := 100
+	page := 1
+	res := make([]model.File, 0)
+	for {
+		offset := (page - 1) * limit
+		query := map[string]string{
+			"path":   path_,
+			"limit":  strconv.Itoa(limit),
+			"offset": strconv.Itoa(offset),
+		}
+		if account.OrderBy != "" {
+			if account.OrderDirection == "desc" {
+				query["sort"] = "-" + account.OrderBy
+			} else {
+				query["sort"] = account.OrderBy
+			}
+		}
+		var resp FilesResp
+		_, err := driver.Request("", base.Get, nil, query, nil, nil, &resp, account)
+		if err != nil {
+			return nil, err
+		}
+		for _, file := range resp.Embedded.Items {
+			f := model.File{
+				Name:      file.Name,
+				Size:      file.Size,
+				Driver:    driver.Config().Name,
+				UpdatedAt: &file.Modified,
+				Thumbnail: file.Preview,
+				Url:       file.File,
+			}
+			if file.Type == "dir" {
+				f.Type = conf.FOLDER
+			} else {
+				f.Type = utils.GetFileType(path.Ext(file.Name))
+			}
+			res = append(res, f)
+		}
+		if resp.Embedded.Total <= offset+limit {
+			break
+		}
+	}
+	return res, nil
+}
+
+func init() {
+	base.RegisterDriver(&Yandex{})
+}
diff --git a/model/account.go b/model/account.go
index f217bc5ec71..9bb04f6b6dd 100644
--- a/model/account.go
+++ b/model/account.go
@@ -2,6 +2,9 @@ package model
 
 import (
 	"github.com/Xhofe/alist/conf"
+	log "github.com/sirupsen/logrus"
+	"strings"
+	"sync"
 	"time"
 )
 
@@ -30,8 +33,9 @@ type Account struct {
 	SiteUrl        string     `json:"site_url"`
 	SiteId         string     `json:"site_id"`
 	InternalType   string     `json:"internal_type"`
-	WebdavProxy    bool       `json:"webdav_proxy"` // 开启之后只会webdav走中转
-	Proxy          bool       `json:"proxy"`        // 是否中转,开启之后web和webdav都会走中转
+	WebdavProxy    bool       `json:"webdav_proxy"`  // 开启之后只会webdav走中转
+	Proxy          bool       `json:"proxy"`         // 是否中转,开启之后web和webdav都会走中转
+	WebdavDirect   bool       `json:"webdav_direct"` // webdav 下载不跳转
 	//AllowProxy     bool       `json:"allow_proxy"` // 是否允许中转下载
 	DownProxyUrl string `json:"down_proxy_url"` // 用于中转下载服务的URL 两处 1. path请求中返回的链接 2. down下载时进行302
 	APIProxyUrl  string `json:"api_proxy_url"`  // 用于中转api的地址
@@ -45,7 +49,9 @@ type Account struct {
 	ExtractFolder string `json:"extract_folder"`
 }
 
-var accountsMap = map[string]Account{}
+var accountsMap = make(map[string]Account)
+
+var balance = ".balance"
 
 // SaveAccount save account to database
 func SaveAccount(account *Account) error {
@@ -100,6 +106,46 @@ func GetAccount(name string) (Account, bool) {
 	return account, ok
 }
 
+func GetAccountsByName(name string) []Account {
+	accounts := make([]Account, 0)
+	if AccountsCount() == 1 {
+		account, _ := GetAccount("")
+		accounts = append(accounts, account)
+		return accounts
+	}
+	for _, v := range accountsMap {
+		if v.Name == name || v.Name == name+balance {
+			accounts = append(accounts, v)
+		}
+	}
+	return accounts
+}
+
+var balanceMap sync.Map
+
+func GetBalancedAccount(name string) (Account, bool) {
+	accounts := GetAccountsByName(name)
+	accountNum := len(accounts)
+	switch accountNum {
+	case 0:
+		return Account{}, false
+	case 1:
+		return accounts[0], true
+	default:
+		cur, ok := balanceMap.Load(name)
+		if ok {
+			i := cur.(int)
+			i = (i + 1) % accountNum
+			balanceMap.Store(name, i)
+			log.Debugln("use: ", i)
+			return accounts[i], true
+		} else {
+			balanceMap.Store(name, 0)
+			return accounts[0], true
+		}
+	}
+}
+
 func GetAccountById(id uint) (*Account, error) {
 	var account Account
 	account.ID = id
@@ -116,6 +162,9 @@ func GetAccountFiles() ([]File, error) {
 		return nil, err
 	}
 	for _, v := range accounts {
+		if strings.HasSuffix(v.Name, balance) {
+			continue
+		}
 		files = append(files, File{
 			Name:      v.Name,
 			Size:      0,
diff --git a/model/setting.go b/model/setting.go
index dfb57d2da9a..5571bc9419e 100644
--- a/model/setting.go
+++ b/model/setting.go
@@ -97,7 +97,7 @@ func LoadSettings() {
 	favicon, err := GetSettingByKey("favicon")
 	if err == nil {
 		//conf.Favicon = favicon.Value
-		conf.ManageHtml = strings.Replace(conf.RawIndexHtml, "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png", favicon.Value, 1)
+		conf.ManageHtml = strings.Replace(conf.RawIndexHtml, "https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg", favicon.Value, 1)
 	}
 	title, err := GetSettingByKey("title")
 	if err == nil {
@@ -114,7 +114,11 @@ func LoadSettings() {
 	// token
 	adminPassword, err := GetSettingByKey("password")
 	if err == nil {
-		conf.Token = utils.GetMD5Encode(fmt.Sprintf("https://github.com/Xhofe/alist-%s", adminPassword.Value))
+		if adminPassword.Value != "" {
+			conf.Token = utils.GetMD5Encode(fmt.Sprintf("https://github.com/Xhofe/alist-%s", adminPassword.Value))
+		} else {
+			conf.Token = ""
+		}
 	}
 	// load settings
 	for _, key := range conf.LoadSettings {
diff --git a/server/common/common.go b/server/common/common.go
index 745ab87c7c6..60294cbb9d6 100644
--- a/server/common/common.go
+++ b/server/common/common.go
@@ -40,7 +40,7 @@ func ParsePath(rawPath string) (*model.Account, string, base.Driver, error) {
 		path = "/" + strings.Join(paths[2:], "/")
 		name = paths[1]
 	}
-	account, ok := model.GetAccount(name)
+	account, ok := model.GetBalancedAccount(name)
 	if !ok {
 		return nil, "", nil, fmt.Errorf("no [%s] account", name)
 	}
diff --git a/server/common/proxy.go b/server/common/proxy.go
new file mode 100644
index 00000000000..be09224b376
--- /dev/null
+++ b/server/common/proxy.go
@@ -0,0 +1,87 @@
+package common
+
+import (
+	"errors"
+	"fmt"
+	"github.com/Xhofe/alist/drivers/base"
+	"github.com/Xhofe/alist/model"
+	log "github.com/sirupsen/logrus"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"os"
+	"strconv"
+)
+
+var HttpClient = &http.Client{}
+
+func Proxy(w http.ResponseWriter, r *http.Request, link *base.Link, file *model.File) error {
+	// 本机读取数据
+	var err error
+	if link.Data != nil {
+		//c.Data(http.StatusOK, "application/octet-stream", link.Data)
+		defer func() {
+			_ = link.Data.Close()
+		}()
+		w.WriteHeader(http.StatusOK)
+		w.Header().Set("Content-Type", "application/octet-stream")
+		w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
+		w.Header().Set("Content-Length", strconv.FormatInt(file.Size, 10))
+		_, err = io.Copy(w, link.Data)
+		if err != nil {
+			return err
+		}
+		return nil
+	}
+	// 本机文件直接返回文件
+	if link.FilePath != "" {
+		f, err := os.Open(link.FilePath)
+		if err != nil {
+			return err
+		}
+		defer func() {
+			_ = f.Close()
+		}()
+		fileStat, err := os.Stat(link.FilePath)
+		if err != nil {
+			return err
+		}
+		http.ServeContent(w, r, file.Name, fileStat.ModTime(), f)
+		return nil
+	} else {
+		req, err := http.NewRequest(r.Method, link.Url, nil)
+		if err != nil {
+			return err
+		}
+		for h, val := range r.Header {
+			req.Header[h] = val
+		}
+		for _, header := range link.Headers {
+			req.Header.Set(header.Name, header.Value)
+		}
+		res, err := HttpClient.Do(req)
+		if err != nil {
+			return err
+		}
+		defer func() {
+			_ = res.Body.Close()
+		}()
+		log.Debugf("proxy status: %d", res.StatusCode)
+		w.WriteHeader(res.StatusCode)
+		if res.StatusCode >= 400 {
+			all, _ := ioutil.ReadAll(res.Body)
+			msg := string(all)
+			log.Debugln(msg)
+			return errors.New(msg)
+		}
+		for h, v := range res.Header {
+			w.Header()[h] = v
+		}
+		_, err = io.Copy(w, res.Body)
+		if err != nil {
+			return err
+		}
+		return nil
+	}
+}
diff --git a/server/controllers/other.go b/server/controllers/other.go
new file mode 100644
index 00000000000..24cf7dacf41
--- /dev/null
+++ b/server/controllers/other.go
@@ -0,0 +1,10 @@
+package controllers
+
+import (
+	"github.com/Xhofe/alist/conf"
+	"github.com/gin-gonic/gin"
+)
+
+func Favicon(c *gin.Context) {
+	c.Redirect(302, conf.GetStr("favicon"))
+}
diff --git a/server/controllers/proxy.go b/server/controllers/proxy.go
index 67e85356859..6dc41ce82ec 100644
--- a/server/controllers/proxy.go
+++ b/server/controllers/proxy.go
@@ -10,12 +10,7 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/go-resty/resty/v2"
 	log "github.com/sirupsen/logrus"
-	"io"
-	"net/http"
-	"net/url"
-	"os"
 	"path/filepath"
-	"strconv"
 )
 
 func Proxy(c *gin.Context) {
@@ -62,92 +57,17 @@ func Proxy(c *gin.Context) {
 		common.ErrorResp(c, err, 500)
 		return
 	}
-	// 本机读取数据
-	if link.Data != nil {
-		//c.Data(http.StatusOK, "application/octet-stream", link.Data)
-		defer func() {
-			_ = link.Data.Close()
-		}()
-		c.Status(http.StatusOK)
-		c.Header("Content-Type", "application/octet-stream")
-		c.Header("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
-		c.Header("Content-Length", strconv.FormatInt(file.Size, 10))
-		_, err = io.Copy(c.Writer, link.Data)
-		if err != nil {
-			_, _ = c.Writer.WriteString(err.Error())
-		}
-		return
-	}
-	// 本机文件直接返回文件
-	if account.Type == "Native" {
-		// 对于名称为index.html的文件需要特殊处理
-		if utils.Base(rawPath) == "index.html" {
-			file, err := os.Open(link.Url)
-			if err != nil {
-				common.ErrorResp(c, err, 500)
-				return
-			}
-			defer func() {
-				_ = file.Close()
-			}()
-			fileStat, err := os.Stat(link.Url)
-			if err != nil {
-				common.ErrorResp(c, err, 500)
-				return
-			}
-			http.ServeContent(c.Writer, c.Request, utils.Base(rawPath), fileStat.ModTime(), file)
-			return
-		}
-		c.File(link.Url)
-		return
-	} else {
-		if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
-			Text(c, link)
-			return
-		}
-		driver.Proxy(c, account)
-		r := c.Request
-		w := c.Writer
-		//target, err := url.Parse(link.Url)
-		//if err != nil {
-		//	common.ErrorResp(c, err, 500)
-		//	return
-		//}
-		req, err := http.NewRequest("GET", link.Url, nil)
-		if err != nil {
-			common.ErrorResp(c, err, 500)
-			return
-		}
-		for h, val := range r.Header {
-			req.Header[h] = val
-		}
-		res, err := HttpClient.Do(req)
-		if err != nil {
-			common.ErrorResp(c, err, 500)
-			return
-		}
-		defer func() {
-			_ = res.Body.Close()
-		}()
-		log.Debugf("proxy status: %d", res.StatusCode)
-		w.WriteHeader(res.StatusCode)
-		for h, v := range res.Header {
-			w.Header()[h] = v
-		}
-		_, err = io.Copy(w, res.Body)
-		if err != nil {
-			common.ErrorResp(c, err, 500)
-			return
-		}
+	err = common.Proxy(c.Writer, c.Request, link, file)
+	if err != nil {
+		common.ErrorResp(c, err, 500)
 	}
 }
 
 var client *resty.Client
-var HttpClient = &http.Client{}
 
 func init() {
 	client = resty.New()
-	client.SetRetryCount(3)
+	client.SetRetryCount(3).SetTimeout(base.DefaultTimeout)
 }
 
 func Text(c *gin.Context, link *base.Link) {
diff --git a/server/router.go b/server/router.go
index 4fb62f6d0bf..2f0aae0ded2 100644
--- a/server/router.go
+++ b/server/router.go
@@ -15,6 +15,7 @@ func InitApiRouter(r *gin.Engine) {
 	Cors(r)
 	r.GET("/d/*path", middlewares.DownCheck, controllers.Down)
 	r.GET("/p/*path", middlewares.DownCheck, controllers.Proxy)
+	r.GET("/favicon.ico", controllers.Favicon)
 
 	api := r.Group("/api")
 	public := api.Group("/public")
diff --git a/server/webdav/file.go b/server/webdav/file.go
index cecc25c175c..479a16dfd9f 100644
--- a/server/webdav/file.go
+++ b/server/webdav/file.go
@@ -24,8 +24,13 @@ import (
 
 type FileSystem struct{}
 
+var upFileMap = make(map[string]*model.File)
+
 func (fs *FileSystem) File(rawPath string) (*model.File, error) {
 	rawPath = utils.ParsePath(rawPath)
+	if f, ok := upFileMap[rawPath]; ok {
+		return f, nil
+	}
 	if model.AccountsCount() > 1 && rawPath == "/" {
 		now := time.Now()
 		return &model.File{
@@ -45,18 +50,17 @@ func (fs *FileSystem) File(rawPath string) (*model.File, error) {
 
 func (fs *FileSystem) Files(ctx context.Context, rawPath string) ([]model.File, error) {
 	rawPath = utils.ParsePath(rawPath)
+	var files []model.File
+	var err error
 	if model.AccountsCount() > 1 && rawPath == "/" {
-		files, err := model.GetAccountFiles()
+		files, err = model.GetAccountFiles()
+	} else {
+		account, path_, driver, err := common.ParsePath(rawPath)
 		if err != nil {
 			return nil, err
 		}
-		return files, nil
+		files, err = operate.Files(driver, account, path_)
 	}
-	account, path_, driver, err := common.ParsePath(rawPath)
-	if err != nil {
-		return nil, err
-	}
-	files, err := operate.Files(driver, account, path_)
 	if err != nil {
 		return nil, err
 	}
@@ -91,7 +95,7 @@ func ClientIP(r *http.Request) string {
 	return ""
 }
 
-func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
+func (fs *FileSystem) Link(w http.ResponseWriter, r *http.Request, rawPath string) (string, error) {
 	rawPath = utils.ParsePath(rawPath)
 	log.Debugf("get link path: %s", rawPath)
 	if model.AccountsCount() > 1 && rawPath == "/" {
@@ -106,6 +110,19 @@ func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
 	if r.TLS != nil {
 		protocol = "https"
 	}
+	// 直接返回
+	if account.WebdavDirect {
+		file, err := fs.File(rawPath)
+		if err != nil {
+			return "", err
+		}
+		link_, err := driver.Link(base.Args{Path: path_}, account)
+		if err != nil {
+			return "", err
+		}
+		err = common.Proxy(w, r, link_, file)
+		return "", err
+	}
 	if driver.Config().OnlyProxy || account.WebdavProxy {
 		link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
 		if conf.GetBool("check down link") {
@@ -154,6 +171,17 @@ func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath strin
 	//	return err
 	//}
 	filePath, fileName := filepath.Split(path_)
+	now := time.Now()
+	if fileSize == 0 {
+		upFileMap[rawPath] = &model.File{
+			Name:      fileName,
+			Size:      0,
+			UpdatedAt: &now,
+		}
+		return nil
+	} else {
+		delete(upFileMap, rawPath)
+	}
 	fileData := model.FileStream{
 		MIMEType:   r.Header.Get("Content-Type"),
 		File:       r.Body,
diff --git a/server/webdav/webdav.go b/server/webdav/webdav.go
index 05550c914c6..cc24309bce6 100644
--- a/server/webdav/webdav.go
+++ b/server/webdav/webdav.go
@@ -46,7 +46,7 @@ func (h *Handler) stripPrefix(p string) (string, int, error) {
 func isPathExist(ctx context.Context, fs *FileSystem, path string) (bool, FileInfo) {
 	file, err := fs.File(path)
 	if err != nil {
-		log.Debug(path, err)
+		log.Debugln(path, err)
 		return false, nil
 	}
 	return true, file
@@ -237,11 +237,14 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *
 	}
 	w.Header().Set("ETag", etag)
 	log.Debugf("url: %+v", r.URL)
-	link, err := fs.Link(r, reqPath)
+	link, err := fs.Link(w, r, reqPath)
+	log.Debugf("webdav link error: %s", err.Error())
 	if err != nil {
 		return http.StatusInternalServerError, err
 	}
-	http.Redirect(w, r, link, 302)
+	if link != "" {
+		http.Redirect(w, r, link, 302)
+	}
 	return 0, nil
 }
 
diff --git a/utils/file.go b/utils/file.go
index 0608b98328f..cfeaa049838 100644
--- a/utils/file.go
+++ b/utils/file.go
@@ -123,3 +123,7 @@ func Join(elem ...string) string {
 	}
 	return res
 }
+
+func Split(p string) (string, string) {
+	return path.Split(p)
+}
diff --git a/utils/random.go b/utils/random.go
new file mode 100644
index 00000000000..0ac2422688c
--- /dev/null
+++ b/utils/random.go
@@ -0,0 +1,25 @@
+package utils
+
+import (
+	"math/rand"
+	"strings"
+	"time"
+)
+
+var Rand *rand.Rand
+
+func init() {
+	s := rand.NewSource(time.Now().UnixNano())
+	Rand = rand.New(s)
+}
+
+func RandomStr(n int) string {
+
+	builder := strings.Builder{}
+	t := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+	for i := 0; i < n; i++ {
+		r := Rand.Intn(len(t))
+		builder.WriteString(t[r : r+1])
+	}
+	return builder.String()
+}


Test patch: