fix(189pc): large file upload error (close #4417 in #4438)

pull/4459/head
foxxorcat 2023-05-27 14:28:58 +08:00 committed by GitHub
parent b2f5757f8d
commit 2c1f70fbe9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 417 additions and 114 deletions

View File

@ -4,7 +4,6 @@ import (
"context"
"net/http"
"strings"
"time"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver"
@ -19,8 +18,7 @@ type Cloud189PC struct {
identity string
client *resty.Client
putClient *resty.Client
client *resty.Client
loginParam *LoginParam
tokenInfo *AppSessionResp
@ -51,9 +49,6 @@ func (y *Cloud189PC) Init(ctx context.Context) (err error) {
"Referer": WEB_URL,
})
}
if y.putClient == nil {
y.putClient = base.NewRestyClient().SetTimeout(120 * time.Second)
}
// 避免重复登陆
identity := utils.GetMD5Encode(y.Username + y.Password)
@ -266,8 +261,14 @@ func (y *Cloud189PC) Remove(ctx context.Context, obj model.Obj) error {
}
func (y *Cloud189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
if y.RapidUpload {
switch y.UploadMethod {
case "stream":
return y.CommonUpload(ctx, dstDir, stream, up)
case "old":
return y.OldUpload(ctx, dstDir, stream, up)
case "rapid":
return y.FastUpload(ctx, dstDir, stream, up)
default:
return y.CommonUpload(ctx, dstDir, stream, up)
}
return y.CommonUpload(ctx, dstDir, stream, up)
}

View File

@ -11,6 +11,7 @@ import (
"encoding/hex"
"encoding/pem"
"fmt"
"math"
"net/http"
"regexp"
"strings"
@ -131,3 +132,18 @@ func BoolToNumber(b bool) int {
}
return 0
}
// 计算分片大小
// 对分片数量有限制
// 10MIB 20 MIB 999片
// 50MIB 60MIB 70MIB 80MIB ∞MIB 1999片
func partSize(size int64) int64 {
const DEFAULT = 1024 * 1024 * 10 // 10MIB
if size > DEFAULT*2*999 {
return int64(math.Max(math.Ceil((float64(size)/1999) /*=单个切片大小*/ /float64(DEFAULT)) /*=倍率*/, 5) * DEFAULT)
}
if size > DEFAULT*999 {
return DEFAULT * 2 // 20MIB
}
return DEFAULT
}

View File

@ -14,13 +14,14 @@ type Addition struct {
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
Type string `json:"type" type:"select" options:"personal,family" default:"personal"`
FamilyID string `json:"family_id"`
RapidUpload bool `json:"rapid_upload"`
UploadMethod string `json:"upload_method" type:"select" options:"stream,rapid,old" default:"stream"`
NoUseOcr bool `json:"no_use_ocr"`
}
var config = driver.Config{
Name: "189CloudPC",
DefaultRoot: "-11",
CheckStatus: true,
}
func init() {

View File

@ -10,20 +10,62 @@ import (
// 居然有四种返回方式
type RespErr struct {
ResCode string `json:"res_code"`
ResCode any `json:"res_code"` // int or string
ResMessage string `json:"res_message"`
Error_ string `json:"error"`
XMLName xml.Name `xml:"error"`
Code string `json:"code" xml:"code"`
Message string `json:"message" xml:"message"`
// Code string `json:"code"`
Msg string `json:"msg"`
Msg string `json:"msg"`
ErrorCode string `json:"errorCode"`
ErrorMsg string `json:"errorMsg"`
}
func (e *RespErr) HasError() bool {
switch v := e.ResCode.(type) {
case int, int64, int32:
return v != 0
case string:
return e.ResCode != ""
}
return (e.Code != "" && e.Code != "SUCCESS") || e.ErrorCode != "" || e.Error_ != ""
}
func (e *RespErr) Error() string {
switch v := e.ResCode.(type) {
case int, int64, int32:
if v != 0 {
return fmt.Sprintf("res_code: %d ,res_msg: %s", v, e.ResMessage)
}
case string:
if e.ResCode != "" {
return fmt.Sprintf("res_code: %s ,res_msg: %s", e.ResCode, e.ResMessage)
}
}
if e.Code != "" && e.Code != "SUCCESS" {
if e.Msg != "" {
return fmt.Sprintf("code: %s ,msg: %s", e.Code, e.Msg)
}
if e.Message != "" {
return fmt.Sprintf("code: %s ,msg: %s", e.Code, e.Message)
}
return "code: " + e.Code
}
if e.ErrorCode != "" {
return fmt.Sprintf("err_code: %s ,err_msg: %s", e.ErrorCode, e.ErrorMsg)
}
if e.Error_ != "" {
return fmt.Sprintf("error: %s ,message: %s", e.ErrorCode, e.Message)
}
return ""
}
// 登陆需要的参数
type LoginParam struct {
// 加密后的用户名和密码
@ -218,6 +260,42 @@ type Part struct {
RequestHeader string `json:"requestHeader"`
}
/* 第二种上传方式 */
type CreateUploadFileResp struct {
// 上传文件请求ID
UploadFileId int64 `json:"uploadFileId"`
// 上传文件数据的URL路径
FileUploadUrl string `json:"fileUploadUrl"`
// 上传文件完成后确认路径
FileCommitUrl string `json:"fileCommitUrl"`
// 文件是否已存在云盘中0-未存在1-已存在
FileDataExists int `json:"fileDataExists"`
}
type GetUploadFileStatusResp struct {
CreateUploadFileResp
// 已上传的大小
DataSize int64 `json:"dataSize"`
Size int64 `json:"size"`
}
func (r *GetUploadFileStatusResp) GetSize() int64 {
return r.DataSize + r.Size
}
type CommitUploadFileResp struct {
XMLName xml.Name `xml:"file"`
Id string `xml:"id"`
Name string `xml:"name"`
Size string `xml:"size"`
Md5 string `xml:"md5"`
CreateDate string `xml:"createDate"`
Rev string `xml:"rev"`
UserId string `xml:"userId"`
}
/* query 加密参数*/
type Params map[string]string
func (p Params) Set(k, v string) {

View File

@ -6,6 +6,7 @@ import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
"encoding/xml"
"fmt"
"io"
"math"
@ -15,6 +16,7 @@ import (
"os"
"regexp"
"strings"
"time"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/conf"
@ -23,9 +25,12 @@ import (
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/internal/setting"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/avast/retry-go"
"github.com/go-resty/resty/v2"
"github.com/google/uuid"
jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
)
const (
@ -47,7 +52,7 @@ const (
CHANNEL_ID = "web_cloud.189.cn"
)
func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, params Params, resp interface{}) ([]byte, error) {
func (y *Cloud189PC) SignatureHeader(url, method, params string) map[string]string {
dateOfGmt := getHttpDateStr()
sessionKey := y.tokenInfo.SessionKey
sessionSecret := y.tokenInfo.SessionSecret
@ -56,19 +61,40 @@ func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, para
sessionSecret = y.tokenInfo.FamilySessionSecret
}
req := y.client.R().SetQueryParams(clientSuffix()).SetHeaders(map[string]string{
header := map[string]string{
"Date": dateOfGmt,
"SessionKey": sessionKey,
"X-Request-ID": uuid.NewString(),
})
"Signature": signatureOfHmac(sessionSecret, sessionKey, method, url, dateOfGmt, params),
}
return header
}
func (y *Cloud189PC) EncryptParams(params Params) string {
sessionSecret := y.tokenInfo.SessionSecret
if y.isFamily() {
sessionSecret = y.tokenInfo.FamilySessionSecret
}
if params != nil {
return AesECBEncrypt(params.Encode(), sessionSecret[:16])
}
return ""
}
func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, params Params, resp interface{}) ([]byte, error) {
req := y.client.R().SetQueryParams(clientSuffix())
// 设置params
var paramsData string
if params != nil {
paramsData = AesECBEncrypt(params.Encode(), sessionSecret[:16])
paramsData := y.EncryptParams(params)
if paramsData != "" {
req.SetQueryParam("params", paramsData)
}
req.SetHeader("Signature", signatureOfHmac(sessionSecret, sessionKey, method, url, dateOfGmt, paramsData))
// Signature
req.SetHeaders(y.SignatureHeader(url, method, paramsData))
var erron RespErr
req.SetError(&erron)
if callback != nil {
callback(req)
@ -80,32 +106,6 @@ func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, para
if err != nil {
return nil, err
}
var erron RespErr
utils.Json.Unmarshal(res.Body(), &erron)
if erron.ResCode != "" {
return nil, fmt.Errorf("res_code: %s ,res_msg: %s", erron.ResCode, erron.ResMessage)
}
if erron.Code != "" && erron.Code != "SUCCESS" {
if erron.Msg != "" {
return nil, fmt.Errorf("code: %s ,msg: %s", erron.Code, erron.Msg)
}
if erron.Message != "" {
return nil, fmt.Errorf("code: %s ,msg: %s", erron.Code, erron.Message)
}
return nil, fmt.Errorf(res.String())
}
switch erron.ErrorCode {
case "":
break
case "InvalidSessionKey":
if err = y.refreshSession(); err != nil {
return nil, err
}
return y.request(url, method, callback, params, resp)
default:
return nil, fmt.Errorf("err_code: %s ,err_msg: %s", erron.ErrorCode, erron.ErrorMsg)
}
if strings.Contains(res.String(), "userSessionBO is null") {
if err = y.refreshSession(); err != nil {
@ -114,14 +114,17 @@ func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, para
return y.request(url, method, callback, params, resp)
}
resCode := utils.Json.Get(res.Body(), "res_code").ToInt64()
message := utils.Json.Get(res.Body(), "res_message").ToString()
switch resCode {
case 0:
return res.Body(), nil
default:
return nil, fmt.Errorf("res_code: %d ,res_msg: %s", resCode, message)
// 处理错误
if erron.HasError() {
if erron.ErrorCode == "InvalidSessionKey" {
if err = y.refreshSession(); err != nil {
return nil, err
}
return y.request(url, method, callback, params, resp)
}
return nil, &erron
}
return res.Body(), nil
}
func (y *Cloud189PC) get(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
@ -132,6 +135,50 @@ func (y *Cloud189PC) post(url string, callback base.ReqCallback, resp interface{
return y.request(url, http.MethodPost, callback, nil, resp)
}
func (y *Cloud189PC) put(ctx context.Context, url string, headers map[string]string, sign bool, file io.Reader) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, file)
if err != nil {
return nil, err
}
query := req.URL.Query()
for key, value := range clientSuffix() {
query.Add(key, value)
}
req.URL.RawQuery = query.Encode()
for key, value := range headers {
req.Header.Add(key, value)
}
if sign {
for key, value := range y.SignatureHeader(url, http.MethodPut, "") {
req.Header.Add(key, value)
}
}
resp, err := base.HttpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var erron RespErr
jsoniter.Unmarshal(body, &erron)
xml.Unmarshal(body, &erron)
if erron.HasError() {
return nil, &erron
}
if resp.StatusCode != http.StatusOK {
return nil, errors.Errorf("put fail,err:%s", string(body))
}
return body, nil
}
func (y *Cloud189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj, error) {
fullUrl := API_URL
if y.isFamily() {
@ -186,7 +233,7 @@ func (y *Cloud189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj,
func (y *Cloud189PC) login() (err error) {
// 初始化登陆所需参数
if y.loginParam == nil || !y.NoUseOcr {
if y.loginParam == nil {
if err = y.initLoginParam(); err != nil {
// 验证码也通过错误返回
return err
@ -197,7 +244,7 @@ func (y *Cloud189PC) login() (err error) {
y.VCode = ""
// 销毁登陆参数
y.loginParam = nil
// 遇到错误,重新加载登陆参数
// 遇到错误,重新加载登陆参数(刷新验证码)
if err != nil && y.NoUseOcr {
if err1 := y.initLoginParam(); err1 != nil {
err = fmt.Errorf("err1: %s \nerr2: %s", err, err1)
@ -249,9 +296,8 @@ func (y *Cloud189PC) login() (err error) {
return
}
if erron.ResCode != "" {
err = fmt.Errorf(erron.ResMessage)
return
if erron.HasError() {
return &erron
}
if tokenInfo.ResCode != 0 {
err = fmt.Errorf(tokenInfo.ResMessage)
@ -304,6 +350,21 @@ func (y *Cloud189PC) initLoginParam() error {
param.RsaPassword = encryptConf.Data.Pre + RsaEncrypt(param.jRsaKey, y.Password)
y.loginParam = &param
// 判断是否需要验证码
resp, err := y.client.R().
SetFormData(map[string]string{
"appKey": APP_ID,
"accountType": ACCOUNT_TYPE,
"userName": param.RsaUsername,
}).Post(AUTH_URL + "/api/logbox/oauth2/needcaptcha.do")
if err != nil {
return err
}
if resp.String() == "0" {
return nil
}
// 拉取验证码
imgRes, err := y.client.R().
SetQueryParams(map[string]string{
"token": param.CaptchaToken,
@ -359,33 +420,23 @@ func (y *Cloud189PC) refreshSession() (err error) {
}
}()
switch erron.ResCode {
case "":
break
case "UserInvalidOpenToken":
if err = y.login(); err != nil {
return err
if erron.HasError() {
if erron.ResCode == "UserInvalidOpenToken" {
if err = y.login(); err != nil {
return err
}
}
default:
err = fmt.Errorf("res_code: %s ,res_msg: %s", erron.ResCode, erron.ResMessage)
return
}
switch userSessionResp.ResCode {
case 0:
y.tokenInfo.UserSessionResp = userSessionResp
default:
err = fmt.Errorf("code: %d , msg: %s", userSessionResp.ResCode, userSessionResp.ResMessage)
return &erron
}
y.tokenInfo.UserSessionResp = userSessionResp
return
}
// 普通上传
func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
const DEFAULT int64 = 10485760
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
var DEFAULT = partSize(file.GetSize())
var count = int(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
requestID := uuid.NewString()
params := Params{
"parentFolderId": dstDir.GetID(),
"fileName": url.QueryEscape(file.GetName()),
@ -407,7 +458,6 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
var initMultiUpload InitMultiUploadResp
_, err = y.request(fullUrl+"/initMultiUpload", http.MethodGet, func(req *resty.Request) {
req.SetContext(ctx)
req.SetHeader("X-Request-ID", requestID)
}, params, &initMultiUpload)
if err != nil {
return err
@ -417,7 +467,7 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
silceMd5 := md5.New()
silceMd5Hexs := make([]string, 0, count)
byteData := bytes.NewBuffer(make([]byte, DEFAULT))
for i := int64(1); i <= count; i++ {
for i := 1; i <= count; i++ {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
@ -440,7 +490,6 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
_, err = y.request(fullUrl+"/getMultiUploadUrls", http.MethodGet,
func(req *resty.Request) {
req.SetContext(ctx)
req.SetHeader("X-Request-ID", requestID)
}, Params{
"partInfo": fmt.Sprintf("%d-%s", i, silceMd5Base64),
"uploadFileId": initMultiUpload.Data.UploadFileID,
@ -451,18 +500,18 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
// 开始上传
uploadData := uploadUrl.UploadUrls[fmt.Sprint("partNumber_", i)]
res, err := y.putClient.R().
SetContext(ctx).
SetQueryParams(clientSuffix()).
SetHeaders(ParseHttpHeader(uploadData.RequestHeader)).
SetBody(byteData).
Put(uploadData.RequestURL)
err = retry.Do(func() error {
_, err := y.put(ctx, uploadData.RequestURL, ParseHttpHeader(uploadData.RequestHeader), false, bytes.NewReader(byteData.Bytes()))
return err
},
retry.Context(ctx),
retry.Attempts(3),
retry.Delay(time.Second),
retry.MaxDelay(5*time.Second))
if err != nil {
return err
}
if res.StatusCode() != http.StatusOK {
return fmt.Errorf("updload fail,msg: %s", res.String())
}
up(int(i * 100 / count))
}
@ -476,7 +525,6 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
_, err = y.request(fullUrl+"/commitMultiUploadFile", http.MethodGet,
func(req *resty.Request) {
req.SetContext(ctx)
req.SetHeader("X-Request-ID", requestID)
}, Params{
"uploadFileId": initMultiUpload.Data.UploadFileID,
"fileMd5": fileMd5Hex,
@ -500,7 +548,7 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
_ = os.Remove(tempFile.Name())
}()
const DEFAULT int64 = 10485760
var DEFAULT = partSize(file.GetSize())
count := int(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
// 优先计算所需信息
@ -531,7 +579,6 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
sliceMd5Hex = strings.ToUpper(utils.GetMD5Encode(strings.Join(silceMd5Hexs, "\n")))
}
requestID := uuid.NewString()
// 检测是否支持快传
params := Params{
"parentFolderId": dstDir.GetID(),
@ -554,7 +601,6 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
var uploadInfo InitMultiUploadResp
_, err = y.request(fullUrl+"/initMultiUpload", http.MethodGet, func(req *resty.Request) {
req.SetContext(ctx)
req.SetHeader("X-Request-ID", requestID)
}, params, &uploadInfo)
if err != nil {
return err
@ -566,7 +612,6 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
_, err = y.request(fullUrl+"/getMultiUploadUrls", http.MethodGet,
func(req *resty.Request) {
req.SetContext(ctx)
req.SetHeader("X-Request-ID", requestID)
}, Params{
"uploadFileId": uploadInfo.Data.UploadFileID,
"partInfo": strings.Join(silceMd5Base64s, ","),
@ -575,26 +620,29 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
return err
}
buf := make([]byte, DEFAULT)
for i := 1; i <= count; i++ {
select {
case <-ctx.Done():
if utils.IsCanceled(ctx) {
return ctx.Err()
default:
}
n, err := io.ReadFull(tempFile, buf)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
return err
}
uploadData := uploadUrls.UploadUrls[fmt.Sprint("partNumber_", i)]
res, err := y.putClient.R().
SetContext(ctx).
SetQueryParams(clientSuffix()).
SetHeaders(ParseHttpHeader(uploadData.RequestHeader)).
SetBody(io.LimitReader(tempFile, DEFAULT)).
Put(uploadData.RequestURL)
err = retry.Do(func() error {
_, err := y.put(ctx, uploadData.RequestURL, ParseHttpHeader(uploadData.RequestHeader), false, bytes.NewReader(buf[:n]))
return err
},
retry.Context(ctx),
retry.Attempts(3),
retry.Delay(time.Second),
retry.MaxDelay(5*time.Second))
if err != nil {
return err
}
if res.StatusCode() != http.StatusOK {
return fmt.Errorf("updload fail,msg: %s", res.String())
}
up(int(i * 100 / count))
}
}
@ -603,7 +651,6 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
_, err = y.request(fullUrl+"/commitMultiUploadFile", http.MethodGet,
func(req *resty.Request) {
req.SetContext(ctx)
req.SetHeader("X-Request-ID", requestID)
}, Params{
"uploadFileId": uploadInfo.Data.UploadFileID,
"isLog": "0",
@ -612,6 +659,137 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
return err
}
func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
// 需要获取完整文件md5,必须支持 io.Seek
tempFile, err := utils.CreateTempFile(file.GetReadCloser())
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()
// 计算md5
fileMd5 := md5.New()
if _, err := io.Copy(fileMd5, tempFile); err != nil {
return err
}
if _, err = tempFile.Seek(0, io.SeekStart); err != nil {
return err
}
fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil)))
// 创建上传会话
var uploadInfo CreateUploadFileResp
fullUrl := API_URL + "/createUploadFile.action"
if y.isFamily() {
fullUrl = API_URL + "/family/file/createFamilyFile.action"
}
_, err = y.post(fullUrl, func(req *resty.Request) {
req.SetContext(ctx)
if y.isFamily() {
req.SetQueryParams(map[string]string{
"familyId": y.FamilyID,
"fileMd5": fileMd5Hex,
"fileName": file.GetName(),
"fileSize": fmt.Sprint(file.GetSize()),
"parentId": dstDir.GetID(),
"resumePolicy": "1",
})
} else {
req.SetFormData(map[string]string{
"parentFolderId": dstDir.GetID(),
"fileName": file.GetName(),
"size": fmt.Sprint(file.GetSize()),
"md5": fileMd5Hex,
"opertype": "3",
"flag": "1",
"resumePolicy": "1",
"isLog": "0",
// "baseFileId": "",
// "lastWrite":"",
// "localPath": strings.ReplaceAll(param.LocalPath, "\\", "/"),
// "fileExt": "",
})
}
}, &uploadInfo)
if err != nil {
return err
}
// 网盘中不存在该文件,开始上传
status := GetUploadFileStatusResp{CreateUploadFileResp: uploadInfo}
for status.Size < file.GetSize() && status.FileDataExists != 1 {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
header := map[string]string{
"ResumePolicy": "1",
"Expect": "100-continue",
}
if y.isFamily() {
header["FamilyId"] = fmt.Sprint(y.FamilyID)
header["UploadFileId"] = fmt.Sprint(status.UploadFileId)
} else {
header["Edrive-UploadFileId"] = fmt.Sprint(status.UploadFileId)
}
_, err := y.put(ctx, status.FileUploadUrl, header, true, io.NopCloser(tempFile))
if err, ok := err.(*RespErr); ok && err.Code != "InputStreamReadError" {
return err
}
// 获取断点状态
fullUrl := API_URL + "/getUploadFileStatus.action"
if y.isFamily() {
fullUrl = API_URL + "/family/file/getFamilyFileStatus.action"
}
_, err = y.get(fullUrl, func(req *resty.Request) {
req.SetContext(ctx).SetQueryParams(map[string]string{
"uploadFileId": fmt.Sprint(status.UploadFileId),
"resumePolicy": "1",
})
if y.isFamily() {
req.SetQueryParam("familyId", fmt.Sprint(y.FamilyID))
}
}, &status)
if err != nil {
return err
}
if _, err := tempFile.Seek(status.GetSize(), io.SeekStart); err != nil {
return err
}
up(int(status.Size / file.GetSize()))
}
// 提交
var resp CommitUploadFileResp
_, err = y.post(status.FileCommitUrl, func(req *resty.Request) {
req.SetContext(ctx)
if y.isFamily() {
req.SetHeaders(map[string]string{
"ResumePolicy": "1",
"UploadFileId": fmt.Sprint(status.UploadFileId),
"FamilyId": fmt.Sprint(y.FamilyID),
})
} else {
req.SetFormData(map[string]string{
"opertype": "3",
"resumePolicy": "1",
"uploadFileId": fmt.Sprint(status.UploadFileId),
"isLog": "0",
})
}
}, &resp)
return err
}
func (y *Cloud189PC) isFamily() bool {
return y.Type == "family"
}

View File

@ -18,6 +18,7 @@ type LanZou struct {
Addition
model.Storage
uid string
vei string
}
func (d *LanZou) Config() driver.Config {
@ -28,7 +29,7 @@ func (d *LanZou) GetAddition() driver.Additional {
return &d.Addition
}
func (d *LanZou) Init(ctx context.Context) error {
func (d *LanZou) Init(ctx context.Context) (err error) {
if d.IsCookie() {
if d.RootFolderID == "" {
d.RootFolderID = "-1"
@ -38,8 +39,9 @@ func (d *LanZou) Init(ctx context.Context) error {
return fmt.Errorf("cookie does not contain ylogin")
}
d.uid = ylogin[1]
d.vei, err = d.getVei()
}
return nil
return
}
func (d *LanZou) Drop(ctx context.Context) error {

View File

@ -22,12 +22,17 @@ var once sync.Once
func (d *LanZou) doupload(callback base.ReqCallback, resp interface{}) ([]byte, error) {
return d.post(d.BaseUrl+"/doupload.php", func(req *resty.Request) {
req.SetQueryParam("uid", d.uid)
callback(req)
req.SetQueryParams(map[string]string{
"uid": d.uid,
"vei": d.vei,
})
if callback != nil {
callback(req)
}
}, resp)
}
func (d *LanZou) get(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
func (d *LanZou) get(url string, callback base.ReqCallback) ([]byte, error) {
return d.request(url, http.MethodGet, callback, false)
}
@ -224,7 +229,7 @@ func (d *LanZou) getShareUrlHtml(shareID string) (string, error) {
Value: vs,
})
}
}, nil)
})
if err != nil {
return "", err
}
@ -315,7 +320,7 @@ func (d *LanZou) getFilesByShareUrl(shareID, pwd string, sharePageData string) (
log.Errorf("lanzou: err => not find file page param ,data => %s\n", sharePageData)
return nil, fmt.Errorf("not find file page param")
}
data, err := d.get(fmt.Sprint(d.ShareUrl, urlpaths[1]), nil, nil)
data, err := d.get(fmt.Sprint(d.ShareUrl, urlpaths[1]), nil)
if err != nil {
return nil, err
}
@ -445,3 +450,22 @@ func (d *LanZou) getFileRealInfo(downURL string) (*int64, *time.Time) {
size, _ := strconv.ParseInt(res.Header().Get("Content-Length"), 10, 64)
return &size, &time
}
func (d *LanZou) getVei() (string, error) {
resp, err := d.get("https://pc.woozooo.com/mydisk.php", func(req *resty.Request) {
req.SetQueryParams(map[string]string{
"item": "files",
"action": "index",
"u": d.uid,
})
})
if err != nil {
return "", err
}
html := RemoveNotes(string(resp))
data, err := htmlJsonToMap(html)
if err != nil {
return "", err
}
return data["vei"], nil
}

1
go.mod
View File

@ -47,6 +47,7 @@ require (
github.com/aead/ecdh v0.2.0 // indirect
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible // indirect
github.com/andreburgaud/crypt2go v1.1.0 // indirect
github.com/avast/retry-go v3.0.0+incompatible // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/bits-and-blooms/bitset v1.2.0 // indirect
github.com/blevesearch/bleve_index_api v1.0.5 // indirect

2
go.sum
View File

@ -12,6 +12,8 @@ github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible h1:QoRMR0TCctLDqBCMyOu1e
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andreburgaud/crypt2go v1.1.0 h1:eitZxTPY1krUsxinsng3Qvt/Ud7q/aQmmYRh8p4hyPw=
github.com/andreburgaud/crypt2go v1.1.0/go.mod h1:4qhZPzarj1dCIRmCkpdgCklwp+hBq9yEt0zPe9Ayuhc=
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.38.20/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.44.194 h1:1ZDK+QDcc5oRbZGgRZSz561eR8XVizXCeGpoZKo33NU=
github.com/aws/aws-sdk-go v1.44.194/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=