|
|
|
@ -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 = ¶m
|
|
|
|
|
|
|
|
|
|
// 判断是否需要验证码
|
|
|
|
|
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"
|
|
|
|
|
}
|
|
|
|
|