Repository: AlistGo/alist (language: go)

Issue:
使用rclone同步和彩云与OneDrive出现OOM的情况
### Please make sure of the following things

- [X] I have read the [documentation](https://alist-doc.nn.ci).
- [X] I'm sure there are no duplicate issues or discussions.
- [X] I'm sure it's due to `alist` and not something else(such as `Dependencies` or `Operational`).

### Alist Version / Alist 版本

v2.5.1

### Describe the bug / 问题描述

## 环境与配置说明
ubuntu20.04，amd64
2核2g
rclone版本v1.58.0
alist版本2.5.1

***

## 前言
本人为了将OneDrive的一些文件同步到和彩云上，使用了rclone+alist的方法。（因为rclone的sync命令真的很香）

首先使用rclone配置OneDrive和alist的webdav，然后输入命令：
```bash
rclone sync "od:/XXXXX" "alist:/cloud139/XXXXXX" -P --cache-chunk-total-size=100M --cache-chunk-size=30M --transfers=3
```

需要同步的文件夹大小为19.8g，其中绝大多数为图片（7000多张），以下是od在win10下的同步文件夹属性截图

![image](https://user-images.githubusercontent.com/56548141/167465675-7c1f8769-ba3c-4a54-91ca-6071c89831d1.png)

（由于该文件夹带有一定私密性，请原谅我不予展示）

## 问题描述

前言所述的操作刚开始是正常同步的，大概同步了1个半小时，我发现ssh不上去服务器了，vnc后发现OOM了

![image](https://user-images.githubusercontent.com/56548141/167466758-e38e97e1-6988-4d12-86de-15b2f0fad87b.png)

在OOM之前，rclone的同步命令也是一直提示同步失败，错误代码409
![image](https://user-images.githubusercontent.com/56548141/167468154-7c06f1d1-4860-4a0c-9811-237aee428920.png)

当时我初步诊断为rclone的同步命令参数设置的太高了，于是我重启了alist的服务，并设置`--cache-chunk-total-size=10M`和`--cache-chunk-size=3M`重新跑同步命令

此时的alist内存占用在htop面板下是这样的
![image](https://user-images.githubusercontent.com/56548141/167469776-fe5656bc-7803-4863-bd94-a75c70b2e810.png)

大概过了10几分钟我再去看，发现alist的内存占用极高。
![image](https://user-images.githubusercontent.com/56548141/167472131-fc8d59ec-e67e-415b-abcb-f65803def9f1.png)
我尝试进入网页端手动清理缓存，发现内存并没有降下来，无奈只有重启alist服务，很显然，rclone的同步命令也不得不中止掉。

## 后续补充

再之后，我尝试在我另一台的服务器（ubuntu20.04，1核1g）上跑，这次同步的是一些较大的文件（大部分是日漫视频），rclone的同步命令内存参数也设置的很小，同样出现了类似的情况。
![image](https://user-images.githubusercontent.com/56548141/167472688-02194476-f8e1-4ef6-9139-6c47fa3d6d1b.png)

## 其他补充

我之前也试过rclone只配置alist，然后仅通过alist来进行同步，但是大多数情况下会出现405错误，或者直接找不到文件了。
![image](https://user-images.githubusercontent.com/56548141/167473486-c0ccb101-5460-4870-9089-d766a3039e69.png)
如果rclone配置OneDrive和alist的话这种情况就会大大缓解（不是完全解决，仍然有少数文件会出现这类情况，这时只需要在同步命令执行完成后再执行一次同步命令即可）





### Reproduction / 复现链接

无

### Logs / 日志

_No response_


Discussion / hints:

Fix patch:
diff --git a/drivers/139/driver.go b/drivers/139/driver.go
index b0025fcc897..8864787c3a1 100644
--- a/drivers/139/driver.go
+++ b/drivers/139/driver.go
@@ -448,6 +448,7 @@ func (driver Cloud139) Upload(file *model.FileStream, account *model.Account) er
 			return err
 		}
 		log.Debugf("%+v", res)
+		res.Body.Close()
 		start += byteSize
 	}
 	return nil
diff --git a/drivers/189/189.go b/drivers/189/189.go
index f921a749d15..ac6ea189dd0 100644
--- a/drivers/189/189.go
+++ b/drivers/189/189.go
@@ -577,6 +577,7 @@ func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account)
 
 		r, err := base.HttpClient.Do(req)
 		log.Debugf("%+v %+v", r, r.Request.Header)
+		r.Body.Close()
 		if err != nil {
 			return err
 		}
diff --git a/drivers/189pc/driver.go b/drivers/189pc/driver.go
index b6ca7a1bd30..22b6edeb166 100644
--- a/drivers/189pc/driver.go
+++ b/drivers/189pc/driver.go
@@ -619,8 +619,10 @@ func (driver Cloud189) CommonUpload(file *model.FileStream, parentFile *model.Fi
 		}
 		if r.StatusCode != http.StatusOK {
 			data, _ := io.ReadAll(r.Body)
+			r.Body.Close()
 			return fmt.Errorf(string(data))
 		}
+		r.Body.Close()
 	}
 
 	fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil)))
@@ -727,8 +729,10 @@ func (driver Cloud189) FastUpload(file *model.FileStream, parentFile *model.File
 			}
 			if r.StatusCode != http.StatusOK {
 				data, _ := io.ReadAll(r.Body)
+				r.Body.Close()
 				return fmt.Errorf(string(data))
 			}
+			r.Body.Close()
 		}
 	}
 
diff --git a/drivers/alidrive/driver.go b/drivers/alidrive/driver.go
index 9ad815662bf..013d9a49f31 100644
--- a/drivers/alidrive/driver.go
+++ b/drivers/alidrive/driver.go
@@ -424,10 +424,17 @@ func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) er
 	}
 
 	if account.Bool1 {
-		buf := make([]byte, 1024)
-		n, _ := file.Read(buf[:])
-		reqBody["pre_hash"] = utils.GetSHA1Encode(string(buf[:n]))
-		file.File = io.NopCloser(io.MultiReader(bytes.NewReader(buf[:n]), file.File))
+		buf := bytes.NewBuffer(make([]byte, 0, 1024))
+		io.CopyN(buf, file, 1024)
+		reqBody["pre_hash"] = utils.GetSHA1Encode(buf.String())
+		// 把头部拼接回去
+		file.File = struct {
+			io.Reader
+			io.Closer
+		}{
+			Reader: io.MultiReader(buf, file.File),
+			Closer: file.File,
+		}
 	} else {
 		reqBody["content_hash_name"] = "none"
 		reqBody["proof_version"] = "v1"
@@ -454,7 +461,7 @@ func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) er
 		return fmt.Errorf("%s", e.Message)
 	}
 
-	if e.Code == "PreHashMatched" && account.Bool1 {
+	if account.Bool1 && e.Code == "PreHashMatched" {
 		tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
 		if err != nil {
 			return err
@@ -499,6 +506,7 @@ func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) er
 			return nil
 		}
 
+		// 秒传失败
 		if _, err = tempFile.Seek(0, io.SeekStart); err != nil {
 			return err
 		}
@@ -515,6 +523,7 @@ func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) er
 			return err
 		}
 		log.Debugf("%+v", res)
+		res.Body.Close()
 		//res, err := base.BaseClient.R().
 		//	SetHeader("Content-Type","").
 		//	SetBody(byteData).Put(resp.PartInfoList[i].UploadUrl)
diff --git a/drivers/baiduphoto/baidu.go b/drivers/baiduphoto/baidu.go
index 287ec433ef6..c192939b3f4 100644
--- a/drivers/baiduphoto/baidu.go
+++ b/drivers/baiduphoto/baidu.go
@@ -87,7 +87,7 @@ func (driver Baidu) GetAllFile(account *model.Account) (files []File, err error)
 
 	for {
 		var resp FileListResp
-		_, err = driver.Request(http.MethodGet, FILE_API_URL+"/list", func(r *resty.Request) {
+		_, err = driver.Request(http.MethodGet, FILE_API_URL_V1+"/list", func(r *resty.Request) {
 			r.SetQueryParams(map[string]string{
 				"need_thumbnail":     "1",
 				"need_filter_hidden": "0",
diff --git a/drivers/baiduphoto/driver.go b/drivers/baiduphoto/driver.go
index 99010ac1d95..833ef89f567 100644
--- a/drivers/baiduphoto/driver.go
+++ b/drivers/baiduphoto/driver.go
@@ -43,6 +43,14 @@ func (driver Baidu) Items() []base.Item {
 			Label: "album_id",
 			Type:  base.TypeString,
 		},
+		{
+			Name:     "internal_type",
+			Label:    "download api",
+			Type:     base.TypeSelect,
+			Required: true,
+			Values:   "file,album",
+			Default:  "album",
+		},
 		{
 			Name:     "client_id",
 			Label:    "client id",
@@ -156,6 +164,13 @@ func (driver Baidu) Files(path string, account *model.Account) ([]model.File, er
 }
 
 func (driver Baidu) Link(args base.Args, account *model.Account) (*base.Link, error) {
+	if account.InternalType == "file" {
+		return driver.LinkFile(args, account)
+	}
+	return driver.LinkAlbum(args, account)
+}
+
+func (driver Baidu) LinkAlbum(args base.Args, account *model.Account) (*base.Link, error) {
 	file, err := driver.File(args.Path, account)
 	if err != nil {
 		return nil, err
@@ -190,6 +205,42 @@ func (driver Baidu) Link(args base.Args, account *model.Account) (*base.Link, er
 	}, nil
 }
 
+func (driver Baidu) LinkFile(args base.Args, account *model.Account) (*base.Link, error) {
+	file, err := driver.File(args.Path, account)
+	if err != nil {
+		return nil, err
+	}
+
+	if !IsAlbumFile(file) {
+		return nil, base.ErrNotSupport
+	}
+
+	album, err := driver.File(utils.Dir(utils.ParsePath(args.Path)), account)
+	if err != nil {
+		return nil, err
+	}
+	// 拷贝到根目录
+	cfile, err := driver.CopyAlbumFile(album.Id, account, file.Id)
+	if err != nil {
+		return nil, err
+	}
+
+	res, err := driver.Request(http.MethodGet, FILE_API_URL_V2+"/download", func(r *resty.Request) {
+		r.SetQueryParams(map[string]string{
+			"fsid": fmt.Sprint(cfile.Fsid),
+		})
+	}, account)
+	if err != nil {
+		return nil, err
+	}
+	return &base.Link{
+		Headers: []base.Header{
+			{Name: "User-Agent", Value: base.UserAgent},
+		},
+		Url: utils.Json.Get(res.Body(), "dlink").ToString(),
+	}, nil
+}
+
 func (driver Baidu) Path(path string, account *model.Account) (*model.File, []model.File, error) {
 	path = utils.ParsePath(path)
 	file, err := driver.File(path, account)
@@ -402,7 +453,7 @@ func (driver Baidu) Upload(file *model.FileStream, account *model.Account) error
 
 	// 预上传
 	var precreateResp PrecreateResp
-	_, err = driver.Request(http.MethodPost, FILE_API_URL+"/precreate", func(r *resty.Request) {
+	_, err = driver.Request(http.MethodPost, FILE_API_URL_V1+"/precreate", func(r *resty.Request) {
 		r.SetFormData(params)
 		r.SetResult(&precreateResp)
 	}, account)
@@ -431,7 +482,7 @@ func (driver Baidu) Upload(file *model.FileStream, account *model.Account) error
 		fallthrough
 	case 2: // 创建文件
 		params["uploadid"] = precreateResp.UploadID
-		_, err = driver.Request(http.MethodPost, FILE_API_URL+"/create", func(r *resty.Request) {
+		_, err = driver.Request(http.MethodPost, FILE_API_URL_V1+"/create", func(r *resty.Request) {
 			r.SetFormData(params)
 			r.SetResult(&precreateResp)
 		}, account)
diff --git a/drivers/baiduphoto/types.go b/drivers/baiduphoto/types.go
index 734b5a538b6..926329d6ad4 100644
--- a/drivers/baiduphoto/types.go
+++ b/drivers/baiduphoto/types.go
@@ -97,7 +97,7 @@ type (
 type (
 	UploadFile struct {
 		FsID           int64  `json:"fs_id"`
-		Size           int    `json:"size"`
+		Size           int64  `json:"size"`
 		Md5            string `json:"md5"`
 		ServerFilename string `json:"server_filename"`
 		Path           string `json:"path"`
diff --git a/drivers/baiduphoto/util.go b/drivers/baiduphoto/util.go
index 5ab40a52c85..2c0542c7938 100644
--- a/drivers/baiduphoto/util.go
+++ b/drivers/baiduphoto/util.go
@@ -13,9 +13,10 @@ import (
 )
 
 const (
-	API_URL       = "https://photo.baidu.com/youai"
-	ALBUM_API_URL = API_URL + "/album/v1"
-	FILE_API_URL  = API_URL + "/file/v1"
+	API_URL         = "https://photo.baidu.com/youai"
+	ALBUM_API_URL   = API_URL + "/album/v1"
+	FILE_API_URL_V1 = API_URL + "/file/v1"
+	FILE_API_URL_V2 = API_URL + "/file/v2"
 )
 
 var (
diff --git a/drivers/onedrive/onedrive.go b/drivers/onedrive/onedrive.go
index 7e4efc71194..77bc38ea1fd 100644
--- a/drivers/onedrive/onedrive.go
+++ b/drivers/onedrive/onedrive.go
@@ -307,8 +307,10 @@ func (driver Onedrive) UploadBig(file *model.FileStream, account *model.Account)
 		res, err := base.HttpClient.Do(req)
 		if res.StatusCode != 201 && res.StatusCode != 202 {
 			data, _ := ioutil.ReadAll(res.Body)
+			res.Body.Close()
 			return errors.New(string(data))
 		}
+		res.Body.Close()
 	}
 	return nil
 }
diff --git a/drivers/xunlei/driver.go b/drivers/xunlei/driver.go
index bd2dddbc3c2..2e5b6325e7e 100644
--- a/drivers/xunlei/driver.go
+++ b/drivers/xunlei/driver.go
@@ -105,7 +105,7 @@ func (driver XunLeiCloud) Items() []base.Item {
 			Label:    "device id",
 			Default:  utils.GetMD5Encode(uuid.NewString()),
 			Type:     base.TypeString,
-			Required: false,
+			Required: true,
 		},
 	}
 }
@@ -118,8 +118,10 @@ func (driver XunLeiCloud) Save(account *model.Account, old *model.Account) error
 	client := GetClient(account)
 	// 指定验证通过的captchaToken
 	if client.captchaToken != "" {
+		client.Lock()
 		client.captchaToken = account.CaptchaToken
 		account.CaptchaToken = ""
+		client.Unlock()
 	}
 
 	if client.token == "" {
diff --git a/drivers/xunlei/xunlei.go b/drivers/xunlei/xunlei.go
index 13acd20230d..a1b1a6565d9 100644
--- a/drivers/xunlei/xunlei.go
+++ b/drivers/xunlei/xunlei.go
@@ -73,6 +73,8 @@ func (c *Client) requestCaptchaToken(action string, meta map[string]string) erro
 		SetBody(&param).
 		SetError(&e).
 		SetResult(&resp).
+		SetHeader("X-Device-Id", c.deviceID).
+		SetQueryParam("client_id", c.clientID).
 		Post(XLUSER_API_URL + "/shield/captcha/init")
 	if err != nil {
 		return err
@@ -133,6 +135,8 @@ func (c *Client) Login(account *model.Account) (err error) {
 			Username:     account.Username,
 			Password:     account.Password,
 		}).
+		SetHeader("X-Device-Id", c.deviceID).
+		SetQueryParam("client_id", c.clientID).
 		Post(url)
 	if err != nil {
 		return err
@@ -184,6 +188,8 @@ func (c *Client) RefreshToken() error {
 			"client_id":     c.clientID,
 			"client_secret": c.clientSecret,
 		}).
+		SetHeader("X-Device-Id", c.deviceID).
+		SetQueryParam("client_id", c.clientID).
 		Post(XLUSER_API_URL + "/auth/token")
 	if err != nil {
 		return err
@@ -211,7 +217,8 @@ func (c *Client) Request(method string, url string, callback func(*resty.Request
 			"X-Captcha-Token": c.captchaToken,
 			"User-Agent":      c.userAgent,
 			"client_id":       c.clientID,
-		}).SetQueryParam("client_id", c.clientID)
+		}).
+		SetQueryParam("client_id", c.clientID)
 	if callback != nil {
 		callback(req)
 	}
diff --git a/drivers/yandex/driver.go b/drivers/yandex/driver.go
index 3957fec9491..5edc5500374 100644
--- a/drivers/yandex/driver.go
+++ b/drivers/yandex/driver.go
@@ -213,7 +213,8 @@ func (driver Yandex) Upload(file *model.FileStream, account *model.Account) erro
 	}
 	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.HttpClient.Do(req)
+	res.Body.Close()
 	//res, err := base.RestyClient.R().
 	//	SetHeader("Content-Length", strconv.FormatUint(file.Size, 10)).
 	//	SetBody(file).Put(resp.Href)
diff --git a/server/static.go b/server/static.go
index c91ea10abe0..c60b98d47c5 100644
--- a/server/static.go
+++ b/server/static.go
@@ -1,14 +1,16 @@
 package server
 
 import (
-	"github.com/Xhofe/alist/conf"
-	"github.com/Xhofe/alist/public"
-	"github.com/gin-gonic/gin"
-	log "github.com/sirupsen/logrus"
 	"io/fs"
 	"io/ioutil"
 	"net/http"
+	"net/http/pprof"
 	"strings"
+
+	"github.com/Xhofe/alist/conf"
+	"github.com/Xhofe/alist/public"
+	"github.com/gin-gonic/gin"
+	log "github.com/sirupsen/logrus"
 )
 
 func InitIndex() {
@@ -48,6 +50,8 @@ func Static(r *gin.Engine) {
 		c.Status(200)
 		if strings.HasPrefix(c.Request.URL.Path, "/@manage") {
 			_, _ = c.Writer.WriteString(conf.ManageHtml)
+		} else if strings.HasPrefix(c.Request.URL.Path, "/debug/pprof") {
+			pprof.Index(c.Writer, c.Request)
 		} else {
 			_, _ = c.Writer.WriteString(conf.IndexHtml)
 		}


Test patch: