Repository: AlistGo/alist (language: go)

Issue:
SFTP模式下，无法删除文件
### Please make sure of the following things

- [X] I have read the [documentation](https://alist-doc.nn.ci).
- [ ] I'm sure there are no duplicate issues or discussions.
- [ ] I'm sure it's due to `alist` and not something else(such as `Dependencies` or `Operational`).

### Alist Version / Alist 版本

v2.5.0

### Describe the bug / 问题描述

![image](https://user-images.githubusercontent.com/49216568/168264778-81c01039-7fd2-4e17-a48a-1c1f71c3efaf.png)
如图，删除Hourglass.flac的请求一直在pending且浏览器console没有报错；
test目录也不能删除，但是如果test是个空目录的话就能删除
PS：确定不是账号权限问题，我使用的root账号，而且文件重命名、复制、移动的操作也是可以的，唯独删除不行

### Reproduction / 复现链接

不方便提供，见谅

### Logs / 日志

_No response_


Discussion / hints:

Fix patch:
diff --git a/bootstrap/setting.go b/bootstrap/setting.go
index 1843325f0df..0927682f171 100644
--- a/bootstrap/setting.go
+++ b/bootstrap/setting.go
@@ -124,6 +124,13 @@ func InitSettings() {
 			Access:      model.PUBLIC,
 			Group:       model.FRONT,
 		},
+		{
+			Key:    "pdf viewer url",
+			Type:   "string",
+			Value:  "https://alist-org.github.io/pdf.js/web/viewer.html?file=$url",
+			Access: model.PUBLIC,
+			Group:  model.FRONT,
+		},
 		{
 			Key:    "autoplay video",
 			Value:  "false",
diff --git a/conf/var.go b/conf/var.go
index cf1e9227439..4efc92eac1b 100644
--- a/conf/var.go
+++ b/conf/var.go
@@ -40,7 +40,7 @@ var (
 		"go", "sh", "c", "cpp", "h", "hpp", "tsx", "vtt", "srt", "ass"}
 	DProxyTypes = []string{"m3u8"}
 	OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"}
-	VideoTypes  = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm", "flv"}
+	VideoTypes  = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm", "flv", "m4v"}
 	AudioTypes  = []string{"mp3", "flac", "ogg", "m4a", "wav", "opus"}
 	ImageTypes  = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg", "ico", "swf", "webp"}
 )
diff --git a/drivers/123/driver.go b/drivers/123/driver.go
index 43cf7cf7efa..47c8b21b200 100644
--- a/drivers/123/driver.go
+++ b/drivers/123/driver.go
@@ -1,7 +1,9 @@
 package _23
 
 import (
+	"bytes"
 	"crypto/md5"
+	"encoding/binary"
 	"encoding/hex"
 	"fmt"
 	"io"
@@ -68,6 +70,12 @@ func (driver Pan123) Items() []base.Item {
 			Required: true,
 			Default:  "asc",
 		},
+		{
+			Name:        "bool_1",
+			Label:       "stream upload",
+			Type:        base.TypeBool,
+			Description: "io stream upload (test)",
+		},
 	}
 }
 
@@ -302,23 +310,44 @@ func (driver Pan123) Upload(file *model.FileStream, account *model.Account) erro
 		return base.ErrNotFolder
 	}
 
-	tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
-	if err != nil {
-		return err
-	}
-	defer tempFile.Close()
-	defer os.Remove(tempFile.Name())
+	const DEFAULT int64 = 10485760
+	var uploadFile io.Reader
 	h := md5.New()
-	if _, err = io.Copy(io.MultiWriter(tempFile, h), file); err != nil {
-		return err
-	}
-	etag := hex.EncodeToString(h.Sum(nil))
+	if account.Bool1 && file.GetSize() > uint64(DEFAULT) {
+		// 只计算前10MIB
+		buf := bytes.NewBuffer(make([]byte, 0, DEFAULT))
+		if n, err := io.CopyN(io.MultiWriter(buf, h), file, DEFAULT); err != io.EOF && n == 0 {
+			return err
+		}
+		// 增加额外参数防止MD5碰撞
+		h.Write([]byte(file.Name))
+		num := make([]byte, 8)
+		binary.BigEndian.PutUint64(num, file.Size)
+		h.Write(num)
+		// 拼装
+		uploadFile = io.MultiReader(buf, file)
+	} else {
+		// 计算完整文件MD5
+		tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
+		if err != nil {
+			return err
+		}
+		defer func() {
+			_ = tempFile.Close()
+			_ = os.Remove(tempFile.Name())
+		}()
 
-	_, err = tempFile.Seek(0, io.SeekStart)
-	if err != nil {
-		return err
-	}
+		if _, err = io.Copy(io.MultiWriter(tempFile, h), file); err != nil {
+			return err
+		}
 
+		_, err = tempFile.Seek(0, io.SeekStart)
+		if err != nil {
+			return err
+		}
+		uploadFile = tempFile
+	}
+	etag := hex.EncodeToString(h.Sum(nil))
 	data := base.Json{
 		"driveId":      0,
 		"duplicate":    2, // 2->覆盖 1->重命名 0->默认
@@ -352,7 +381,7 @@ func (driver Pan123) Upload(file *model.FileStream, account *model.Account) erro
 	input := &s3manager.UploadInput{
 		Bucket: &resp.Data.Bucket,
 		Key:    &resp.Data.Key,
-		Body:   tempFile,
+		Body:   uploadFile,
 	}
 	_, err = uploader.Upload(input)
 	if err != nil {
diff --git a/drivers/189/189.go b/drivers/189/189.go
index ac6ea189dd0..069c13cdc4c 100644
--- a/drivers/189/189.go
+++ b/drivers/189/189.go
@@ -17,7 +17,6 @@ import (
 	"io"
 	"math"
 	"net/http"
-	"net/http/cookiejar"
 	"regexp"
 	"strconv"
 	"strings"
@@ -94,18 +93,11 @@ type LoginResp struct {
 
 // Login refer to PanIndex
 func (driver Cloud189) Login(account *model.Account) error {
-	client, ok := client189Map[account.Name]
-	if !ok {
-		//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/")
-	}
-	// clear cookie
-	jar, _ := cookiejar.New(nil)
-	client.SetCookieJar(jar)
+	client := resty.New()
+	//client.SetCookieJar(cookieJar)
+	client.SetTimeout(base.DefaultTimeout)
+	client.SetRetryCount(3)
+	client.SetHeader("Referer", "https://cloud.189.cn/")
 	url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action"
 	b := ""
 	lt := ""
@@ -131,7 +123,8 @@ func (driver Cloud189) Login(account *model.Account) error {
 		}
 	}
 	if lt == "" {
-		return fmt.Errorf("get page: %s \nstatus: %d \nrequest url: %s", b, res.StatusCode(), res.RawResponse.Request.URL.String())
+		return fmt.Errorf("get page: %s \nstatus: %d \nrequest url: %s\nredirect url: %s",
+			b, res.StatusCode(), res.RawResponse.Request.URL.String(), res.Header().Get("location"))
 	}
 	captchaToken := regexp.MustCompile(`captchaToken' value='(.+?)'`).FindStringSubmatch(b)[1]
 	returnUrl := regexp.MustCompile(`returnUrl = '(.+?)'`).FindStringSubmatch(b)[1]
diff --git a/drivers/189pc/driver.go b/drivers/189pc/driver.go
index e8ea2e1c9f2..5337f79d143 100644
--- a/drivers/189pc/driver.go
+++ b/drivers/189pc/driver.go
@@ -607,7 +607,7 @@ func (driver Cloud189) CommonUpload(file *model.FileStream, parentFile *model.Fi
 
 		uploadData := uploadUrl.UploadUrls[fmt.Sprint("partNumber_", i)]
 		req, _ := http.NewRequest(http.MethodPut, uploadData.RequestURL, byteData)
-		req.Header.Del("User-Agent")
+		req.Header.Set("User-Agent", "")
 		for k, v := range ParseHttpHeader(uploadData.RequestHeader) {
 			req.Header.Set(k, v)
 		}
@@ -650,9 +650,10 @@ func (driver Cloud189) FastUpload(file *model.FileStream, parentFile *model.File
 	if err != nil {
 		return err
 	}
-	defer tempFile.Close()
-	defer os.Remove(tempFile.Name())
-
+	defer func() {
+		_ = tempFile.Close()
+		_ = os.Remove(tempFile.Name())
+	}()
 	// 初始化上传
 	state := GetState(account)
 
@@ -718,7 +719,7 @@ func (driver Cloud189) FastUpload(file *model.FileStream, parentFile *model.File
 		for i := 1; i <= count; i++ {
 			uploadData := uploadUrls.UploadUrls[fmt.Sprint("partNumber_", i)]
 			req, _ := http.NewRequest(http.MethodPut, uploadData.RequestURL, io.NewSectionReader(tempFile, int64(i-1)*DEFAULT, DEFAULT))
-			req.Header.Del("User-Agent")
+			req.Header.Set("User-Agent", "")
 			for k, v := range ParseHttpHeader(uploadData.RequestHeader) {
 				req.Header.Set(k, v)
 			}
@@ -756,8 +757,10 @@ func (driver Cloud189) uploadFamily(file *model.FileStream, parentFile *model.Fi
 		return err
 	}
 
-	defer tempFile.Close()
-	defer os.Remove(tempFile.Name())
+	defer func() {
+			_ = tempFile.Close()
+			_ = os.Remove(tempFile.Name())
+	}()
 
 	fileMd5 := md5.New()
 	if _, err = io.Copy(io.MultiWriter(fileMd5, tempFile), file); err != nil {
@@ -805,8 +808,10 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi
 		return err
 	}
 
-	defer tempFile.Close()
-	defer os.Remove(tempFile.Name())
+	defer func() {
+			_ = tempFile.Close()
+			_ = os.Remove(tempFile.Name())
+	}()
 
 	fileMd5 := md5.New()
 	if _, err = io.Copy(io.MultiWriter(fileMd5, tempFile), file); err != nil {
diff --git a/drivers/alidrive/driver.go b/drivers/alidrive/driver.go
index 013d9a49f31..be964c906d3 100644
--- a/drivers/alidrive/driver.go
+++ b/drivers/alidrive/driver.go
@@ -467,8 +467,10 @@ func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) er
 			return err
 		}
 
-		defer tempFile.Close()
-		defer os.Remove(tempFile.Name())
+		defer func() {
+			_ = tempFile.Close()
+			_ = os.Remove(tempFile.Name())
+		}()
 
 		delete(reqBody, "pre_hash")
 		h := sha1.New()
diff --git a/drivers/base/cache.go b/drivers/base/cache.go
index bde23a17217..b0d34189e6c 100644
--- a/drivers/base/cache.go
+++ b/drivers/base/cache.go
@@ -5,6 +5,7 @@ import (
 	"github.com/Xhofe/alist/model"
 	"github.com/Xhofe/alist/utils"
 	log "github.com/sirupsen/logrus"
+	"strings"
 )
 
 func KeyCache(path string, account *model.Account) string {
@@ -15,6 +16,9 @@ func KeyCache(path string, account *model.Account) string {
 }
 
 func SaveSearchFiles[T model.ISearchFile](key string, obj []T) {
+	if strings.Contains(key, ".balance") {
+		return
+	}
 	err := model.DeleteSearchFilesByPath(key)
 	if err != nil {
 		log.Errorln("failed create search files", err)
diff --git a/drivers/sftp/driver.go b/drivers/sftp/driver.go
index a0b70b1b4fc..a531e826004 100644
--- a/drivers/sftp/driver.go
+++ b/drivers/sftp/driver.go
@@ -195,7 +195,7 @@ func (driver SFTP) Delete(path string, account *model.Account) error {
 	if err != nil {
 		return err
 	}
-	return client.Remove(utils.Join(account.RootFolder, path))
+	return client.remove(utils.Join(account.RootFolder, path))
 }
 
 func (driver SFTP) Upload(file *model.FileStream, account *model.Account) error {
diff --git a/drivers/sftp/sftp.go b/drivers/sftp/sftp.go
index 8115dbd13a9..c314f55b41e 100644
--- a/drivers/sftp/sftp.go
+++ b/drivers/sftp/sftp.go
@@ -49,7 +49,7 @@ func (client *Client) Files(remotePath string) ([]os.FileInfo, error) {
 	return client.ReadDir(remotePath)
 }
 
-func (client *Client) Remove(remotePath string) error {
+func (client *Client) remove(remotePath string) error {
 	f, err := client.Stat(remotePath)
 	if err != nil {
 		return nil
diff --git a/drivers/xunlei/driver.go b/drivers/xunlei/driver.go
index fa9db1cc36d..c2d461ed933 100644
--- a/drivers/xunlei/driver.go
+++ b/drivers/xunlei/driver.go
@@ -363,8 +363,10 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
 			return err
 		}
 
-		defer tempFile.Close()
-		defer os.Remove(tempFile.Name())
+		defer func() {
+			_ = tempFile.Close()
+			_ = os.Remove(tempFile.Name())
+		}()
 
 		gcid, err := getGcid(io.TeeReader(file, tempFile), int64(file.Size))
 		if err != nil {
diff --git a/server/static.go b/server/static.go
index c60b98d47c5..8ef8fd07e97 100644
--- a/server/static.go
+++ b/server/static.go
@@ -50,7 +50,7 @@ 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") {
+		} else if strings.HasPrefix(c.Request.URL.Path, "/debug/pprof") && conf.Debug {
 			pprof.Index(c.Writer, c.Request)
 		} else {
 			_, _ = c.Writer.WriteString(conf.IndexHtml)


Test patch: