| | package PikPakProxy |
| |
|
| | import ( |
| | "bytes" |
| | "crypto/md5" |
| | "crypto/sha1" |
| | "encoding/hex" |
| | "fmt" |
| | "io" |
| | "net/http" |
| | "path/filepath" |
| | "regexp" |
| | "strings" |
| | "sync" |
| | "time" |
| |
|
| | "github.com/alist-org/alist/v3/drivers/base" |
| | "github.com/alist-org/alist/v3/internal/driver" |
| | "github.com/alist-org/alist/v3/internal/model" |
| | "github.com/alist-org/alist/v3/internal/op" |
| | "github.com/alist-org/alist/v3/pkg/utils" |
| | "github.com/aliyun/aliyun-oss-go-sdk/oss" |
| | "github.com/go-resty/resty/v2" |
| | jsoniter "github.com/json-iterator/go" |
| | "github.com/pkg/errors" |
| | ) |
| |
|
| | var AndroidAlgorithms = []string{ |
| | "SOP04dGzk0TNO7t7t9ekDbAmx+eq0OI1ovEx", |
| | "nVBjhYiND4hZ2NCGyV5beamIr7k6ifAsAbl", |
| | "Ddjpt5B/Cit6EDq2a6cXgxY9lkEIOw4yC1GDF28KrA", |
| | "VVCogcmSNIVvgV6U+AochorydiSymi68YVNGiz", |
| | "u5ujk5sM62gpJOsB/1Gu/zsfgfZO", |
| | "dXYIiBOAHZgzSruaQ2Nhrqc2im", |
| | "z5jUTBSIpBN9g4qSJGlidNAutX6", |
| | "KJE2oveZ34du/g1tiimm", |
| | } |
| |
|
| | var WebAlgorithms = []string{ |
| | "C9qPpZLN8ucRTaTiUMWYS9cQvWOE", |
| | "+r6CQVxjzJV6LCV", |
| | "F", |
| | "pFJRC", |
| | "9WXYIDGrwTCz2OiVlgZa90qpECPD6olt", |
| | "/750aCr4lm/Sly/c", |
| | "RB+DT/gZCrbV", |
| | "", |
| | "CyLsf7hdkIRxRm215hl", |
| | "7xHvLi2tOYP0Y92b", |
| | "ZGTXXxu8E/MIWaEDB+Sm/", |
| | "1UI3", |
| | "E7fP5Pfijd+7K+t6Tg/NhuLq0eEUVChpJSkrKxpO", |
| | "ihtqpG6FMt65+Xk+tWUH2", |
| | "NhXXU9rg4XXdzo7u5o", |
| | } |
| |
|
| | var PCAlgorithms = []string{ |
| | "KHBJ07an7ROXDoK7Db", |
| | "G6n399rSWkl7WcQmw5rpQInurc1DkLmLJqE", |
| | "JZD1A3M4x+jBFN62hkr7VDhkkZxb9g3rWqRZqFAAb", |
| | "fQnw/AmSlbbI91Ik15gpddGgyU7U", |
| | "/Dv9JdPYSj3sHiWjouR95NTQff", |
| | "yGx2zuTjbWENZqecNI+edrQgqmZKP", |
| | "ljrbSzdHLwbqcRn", |
| | "lSHAsqCkGDGxQqqwrVu", |
| | "TsWXI81fD1", |
| | "vk7hBjawK/rOSrSWajtbMk95nfgf3", |
| | } |
| |
|
| | const ( |
| | OSSUserAgent = "aliyun-sdk-android/2.9.13(Linux/Android 14/M2004j7ac;UKQ1.231108.001)" |
| | OssSecurityTokenHeaderName = "X-OSS-Security-Token" |
| | ThreadsNum = 10 |
| | ) |
| |
|
| | const ( |
| | AndroidClientID = "YNxT9w7GMdWvEOKa" |
| | AndroidClientSecret = "dbw2OtmVEeuUvIptb1Coyg" |
| | AndroidClientVersion = "1.53.2" |
| | AndroidPackageName = "com.pikcloud.pikpak" |
| | AndroidSdkVersion = "2.0.6.206003" |
| | WebClientID = "YUMx5nI8ZU8Ap8pm" |
| | WebClientSecret = "dbw2OtmVEeuUvIptb1Coyg" |
| | WebClientVersion = "2.0.0" |
| | WebPackageName = "mypikpak.com" |
| | WebSdkVersion = "8.0.3" |
| | PCClientID = "YvtoWO6GNHiuCl7x" |
| | PCClientSecret = "1NIH5R1IEe2pAxZE3hv3uA" |
| | PCClientVersion = "undefined" |
| | PCPackageName = "mypikpak.com" |
| | PCSdkVersion = "8.0.3" |
| | ) |
| |
|
| | func (d *PikPakProxy) login() error { |
| | |
| | if d.Addition.Username == "" || d.Addition.Password == "" { |
| | return errors.New("username or password is empty") |
| | } |
| |
|
| | url := "https://user.mypikpak.net/v1/auth/signin" |
| | |
| | if d.GetCaptchaToken() == "" { |
| | if err := d.RefreshCaptchaTokenInLogin(GetAction(http.MethodPost, url), d.Username); err != nil { |
| | return err |
| | } |
| | } |
| |
|
| | if d.Addition.UseProxy { |
| | if strings.HasSuffix(d.Addition.ProxyUrl, "/") { |
| | url = d.Addition.ProxyUrl + url |
| | } else { |
| | url = d.Addition.ProxyUrl + "/" + url |
| | } |
| |
|
| | } |
| |
|
| | var e ErrResp |
| | res, err := base.RestyClient.SetRetryCount(1).R().SetError(&e).SetBody(base.Json{ |
| | "captcha_token": d.GetCaptchaToken(), |
| | "client_id": d.ClientID, |
| | "client_secret": d.ClientSecret, |
| | "username": d.Username, |
| | "password": d.Password, |
| | }).SetQueryParam("client_id", d.ClientID).Post(url) |
| | if err != nil { |
| | return err |
| | } |
| | if e.ErrorCode != 0 { |
| | return &e |
| | } |
| | data := res.Body() |
| | d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString() |
| | d.AccessToken = jsoniter.Get(data, "access_token").ToString() |
| | d.Common.SetUserID(jsoniter.Get(data, "sub").ToString()) |
| | return nil |
| | } |
| |
|
| | func (d *PikPakProxy) refreshToken(refreshToken string) error { |
| | url := "https://user.mypikpak.net/v1/auth/token" |
| | if d.Addition.UseProxy { |
| | if strings.HasSuffix(d.Addition.ProxyUrl, "/") { |
| | url = d.Addition.ProxyUrl + url |
| | } else { |
| | url = d.Addition.ProxyUrl + "/" + url |
| | } |
| |
|
| | } |
| | var e ErrResp |
| | res, err := base.RestyClient.SetRetryCount(1).R().SetError(&e). |
| | SetHeader("user-agent", "").SetBody(base.Json{ |
| | "client_id": d.ClientID, |
| | "client_secret": d.ClientSecret, |
| | "grant_type": "refresh_token", |
| | "refresh_token": refreshToken, |
| | }).SetQueryParam("client_id", d.ClientID).Post(url) |
| | if err != nil { |
| | d.Status = err.Error() |
| | op.MustSaveDriverStorage(d) |
| | return err |
| | } |
| | if e.ErrorCode != 0 { |
| | if e.ErrorCode == 4126 { |
| | |
| | if d.Addition.Username == "" || d.Addition.Password == "" { |
| | return errors.New("refresh_token invalid, please re-provide refresh_token") |
| | } else { |
| | |
| | return d.login() |
| | } |
| | } |
| | d.Status = e.Error() |
| | op.MustSaveDriverStorage(d) |
| | return errors.New(e.Error()) |
| | } |
| | data := res.Body() |
| | d.Status = "work" |
| | d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString() |
| | d.AccessToken = jsoniter.Get(data, "access_token").ToString() |
| | d.Common.SetUserID(jsoniter.Get(data, "sub").ToString()) |
| | d.Addition.RefreshToken = d.RefreshToken |
| | op.MustSaveDriverStorage(d) |
| | return nil |
| | } |
| |
|
| | func (d *PikPakProxy) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { |
| | req := base.RestyClient.R() |
| | req.SetHeaders(map[string]string{ |
| | |
| | "User-Agent": d.GetUserAgent(), |
| | "X-Device-ID": d.GetDeviceID(), |
| | "X-Captcha-Token": d.GetCaptchaToken(), |
| | }) |
| |
|
| | if d.Addition.UseProxy { |
| | if strings.HasSuffix(d.Addition.ProxyUrl, "/") { |
| | url = d.Addition.ProxyUrl + url |
| | } else { |
| | url = d.Addition.ProxyUrl + "/" + url |
| | } |
| |
|
| | } |
| |
|
| | if d.AccessToken != "" { |
| | req.SetHeader("Authorization", "Bearer "+d.AccessToken) |
| | } |
| |
|
| | if callback != nil { |
| | callback(req) |
| | } |
| | if resp != nil { |
| | req.SetResult(resp) |
| | } |
| | var e ErrResp |
| | req.SetError(&e) |
| | res, err := req.Execute(method, url) |
| | if err != nil { |
| | return nil, err |
| | } |
| |
|
| | switch e.ErrorCode { |
| | case 0: |
| | return res.Body(), nil |
| | case 4122, 4121, 16: |
| | |
| | if err1 := d.refreshToken(d.RefreshToken); err1 != nil { |
| | return nil, err1 |
| | } |
| | return d.request(url, method, callback, resp) |
| | case 9: |
| | if err = d.RefreshCaptchaTokenAtLogin(GetAction(method, url), d.GetUserID()); err != nil { |
| | return nil, err |
| | } |
| | return d.request(url, method, callback, resp) |
| | case 10: |
| | return nil, errors.New(e.ErrorDescription) |
| | default: |
| | return nil, errors.New(e.Error()) |
| | } |
| | } |
| |
|
| | func (d *PikPakProxy) requestWithCaptchaToken(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { |
| |
|
| | if err := d.RefreshCaptchaTokenAtLogin(GetAction(method, url), d.Common.UserID); err != nil { |
| | return nil, err |
| | } |
| |
|
| | data, err := d.request(url, method, func(req *resty.Request) { |
| | req.SetHeaders(map[string]string{ |
| | "User-Agent": d.GetUserAgent(), |
| | "X-Device-ID": d.GetDeviceID(), |
| | "X-Captcha-Token": d.GetCaptchaToken(), |
| | }) |
| | if callback != nil { |
| | callback(req) |
| | } |
| | }, resp) |
| |
|
| | errResp, ok := err.(*ErrResp) |
| |
|
| | if !ok { |
| | return nil, err |
| | } |
| |
|
| | switch errResp.ErrorCode { |
| | case 0: |
| | return data, nil |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | case 9: |
| | if err = d.RefreshCaptchaTokenAtLogin(GetAction(method, url), d.Common.UserID); err != nil { |
| | return nil, err |
| | } |
| | default: |
| | return nil, err |
| | } |
| | return d.request(url, method, callback, resp) |
| | } |
| |
|
| | func (d *PikPakProxy) getFiles(id string) ([]File, error) { |
| | res := make([]File, 0) |
| | pageToken := "first" |
| | for pageToken != "" { |
| | if pageToken == "first" { |
| | pageToken = "" |
| | } |
| | query := map[string]string{ |
| | "parent_id": id, |
| | "thumbnail_size": "SIZE_LARGE", |
| | "with_audit": "true", |
| | "limit": "100", |
| | "filters": `{"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}`, |
| | "page_token": pageToken, |
| | } |
| | var resp Files |
| | _, err := d.request("https://api-drive.mypikpak.net/drive/v1/files", http.MethodGet, func(req *resty.Request) { |
| | req.SetQueryParams(query) |
| | }, &resp) |
| | if err != nil { |
| | return nil, err |
| | } |
| | pageToken = resp.NextPageToken |
| | res = append(res, resp.Files...) |
| | } |
| | return res, nil |
| | } |
| |
|
| | func GetAction(method string, url string) string { |
| | urlpath := regexp.MustCompile(`://[^/]+((/[^/\s?#]+)*)`).FindStringSubmatch(url)[1] |
| | return method + ":" + urlpath |
| | } |
| |
|
| | type Common struct { |
| | client *resty.Client |
| | CaptchaToken string |
| | UserID string |
| | |
| | ClientID string |
| | ClientSecret string |
| | ClientVersion string |
| | PackageName string |
| | Algorithms []string |
| | DeviceID string |
| | UserAgent string |
| | |
| | RefreshCTokenCk func(token string) |
| | |
| | UseProxy bool |
| | |
| | ProxyUrl string |
| | } |
| |
|
| | func generateDeviceSign(deviceID, packageName string) string { |
| |
|
| | signatureBase := fmt.Sprintf("%s%s%s%s", deviceID, packageName, "1", "appkey") |
| |
|
| | sha1Hash := sha1.New() |
| | sha1Hash.Write([]byte(signatureBase)) |
| | sha1Result := sha1Hash.Sum(nil) |
| |
|
| | sha1String := hex.EncodeToString(sha1Result) |
| |
|
| | md5Hash := md5.New() |
| | md5Hash.Write([]byte(sha1String)) |
| | md5Result := md5Hash.Sum(nil) |
| |
|
| | md5String := hex.EncodeToString(md5Result) |
| |
|
| | deviceSign := fmt.Sprintf("div101.%s%s", deviceID, md5String) |
| |
|
| | return deviceSign |
| | } |
| |
|
| | func BuildCustomUserAgent(deviceID, clientID, appName, sdkVersion, clientVersion, packageName, userID string) string { |
| | deviceSign := generateDeviceSign(deviceID, packageName) |
| | var sb strings.Builder |
| |
|
| | sb.WriteString(fmt.Sprintf("ANDROID-%s/%s ", appName, clientVersion)) |
| | sb.WriteString("protocolVersion/200 ") |
| | sb.WriteString("accesstype/ ") |
| | sb.WriteString(fmt.Sprintf("clientid/%s ", clientID)) |
| | sb.WriteString(fmt.Sprintf("clientversion/%s ", clientVersion)) |
| | sb.WriteString("action_type/ ") |
| | sb.WriteString("networktype/WIFI ") |
| | sb.WriteString("sessionid/ ") |
| | sb.WriteString(fmt.Sprintf("deviceid/%s ", deviceID)) |
| | sb.WriteString("providername/NONE ") |
| | sb.WriteString(fmt.Sprintf("devicesign/%s ", deviceSign)) |
| | sb.WriteString("refresh_token/ ") |
| | sb.WriteString(fmt.Sprintf("sdkversion/%s ", sdkVersion)) |
| | sb.WriteString(fmt.Sprintf("datetime/%d ", time.Now().UnixMilli())) |
| | sb.WriteString(fmt.Sprintf("usrno/%s ", userID)) |
| | sb.WriteString(fmt.Sprintf("appname/android-%s ", appName)) |
| | sb.WriteString(fmt.Sprintf("session_origin/ ")) |
| | sb.WriteString(fmt.Sprintf("grant_type/ ")) |
| | sb.WriteString(fmt.Sprintf("appid/ ")) |
| | sb.WriteString(fmt.Sprintf("clientip/ ")) |
| | sb.WriteString(fmt.Sprintf("devicename/Xiaomi_M2004j7ac ")) |
| | sb.WriteString(fmt.Sprintf("osversion/13 ")) |
| | sb.WriteString(fmt.Sprintf("platformversion/10 ")) |
| | sb.WriteString(fmt.Sprintf("accessmode/ ")) |
| | sb.WriteString(fmt.Sprintf("devicemodel/M2004J7AC ")) |
| |
|
| | return sb.String() |
| | } |
| |
|
| | func (c *Common) SetDeviceID(deviceID string) { |
| | c.DeviceID = deviceID |
| | } |
| |
|
| | func (c *Common) SetUserID(userID string) { |
| | c.UserID = userID |
| | } |
| |
|
| | func (c *Common) SetUserAgent(userAgent string) { |
| | c.UserAgent = userAgent |
| | } |
| |
|
| | func (c *Common) SetCaptchaToken(captchaToken string) { |
| | c.CaptchaToken = captchaToken |
| | } |
| | func (c *Common) GetCaptchaToken() string { |
| | return c.CaptchaToken |
| | } |
| |
|
| | func (c *Common) GetUserAgent() string { |
| | return c.UserAgent |
| | } |
| |
|
| | func (c *Common) GetDeviceID() string { |
| | return c.DeviceID |
| | } |
| |
|
| | func (c *Common) GetUserID() string { |
| | return c.UserID |
| | } |
| |
|
| | |
| | func (d *PikPakProxy) RefreshCaptchaTokenAtLogin(action, userID string) error { |
| | metas := map[string]string{ |
| | "client_version": d.ClientVersion, |
| | "package_name": d.PackageName, |
| | "user_id": userID, |
| | } |
| | metas["timestamp"], metas["captcha_sign"] = d.Common.GetCaptchaSign() |
| | return d.refreshCaptchaToken(action, metas) |
| | } |
| |
|
| | |
| | func (d *PikPakProxy) RefreshCaptchaTokenInLogin(action, username string) error { |
| | metas := make(map[string]string) |
| | if ok, _ := regexp.MatchString(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`, username); ok { |
| | metas["email"] = username |
| | } else if len(username) >= 11 && len(username) <= 18 { |
| | metas["phone_number"] = username |
| | } else { |
| | metas["username"] = username |
| | } |
| | return d.refreshCaptchaToken(action, metas) |
| | } |
| |
|
| | |
| | func (c *Common) GetCaptchaSign() (timestamp, sign string) { |
| | timestamp = fmt.Sprint(time.Now().UnixMilli()) |
| | str := fmt.Sprint(c.ClientID, c.ClientVersion, c.PackageName, c.DeviceID, timestamp) |
| | for _, algorithm := range c.Algorithms { |
| | str = utils.GetMD5EncodeStr(str + algorithm) |
| | } |
| | sign = "1." + str |
| | return |
| | } |
| |
|
| | |
| | func (d *PikPakProxy) refreshCaptchaToken(action string, metas map[string]string) error { |
| | param := CaptchaTokenRequest{ |
| | Action: action, |
| | CaptchaToken: d.GetCaptchaToken(), |
| | ClientID: d.ClientID, |
| | DeviceID: d.GetDeviceID(), |
| | Meta: metas, |
| | RedirectUri: "xlaccsdk01://xbase.cloud/callback?state=harbor", |
| | } |
| | var e ErrResp |
| | var resp CaptchaTokenResponse |
| | _, err := d.request("https://user.mypikpak.net/v1/shield/captcha/init", http.MethodPost, func(req *resty.Request) { |
| | req.SetError(&e).SetBody(param).SetQueryParam("client_id", d.ClientID) |
| | }, &resp) |
| |
|
| | if err != nil { |
| | return err |
| | } |
| |
|
| | if e.IsError() { |
| | return errors.New(e.Error()) |
| | } |
| |
|
| | if resp.Url != "" { |
| | return fmt.Errorf(`need verify: <a target="_blank" href="%s">Click Here</a>`, resp.Url) |
| | } |
| |
|
| | if d.Common.RefreshCTokenCk != nil { |
| | d.Common.RefreshCTokenCk(resp.CaptchaToken) |
| | } |
| | d.Common.SetCaptchaToken(resp.CaptchaToken) |
| | return nil |
| | } |
| |
|
| | func (d *PikPakProxy) UploadByOSS(params *S3Params, stream model.FileStreamer, up driver.UpdateProgress) error { |
| | ossClient, err := oss.New(params.Endpoint, params.AccessKeyID, params.AccessKeySecret) |
| | if err != nil { |
| | return err |
| | } |
| | bucket, err := ossClient.Bucket(params.Bucket) |
| | if err != nil { |
| | return err |
| | } |
| |
|
| | err = bucket.PutObject(params.Key, stream, OssOption(params)...) |
| | if err != nil { |
| | return err |
| | } |
| | return nil |
| | } |
| | func (d *PikPakProxy) UploadByMultipart(params *S3Params, fileSize int64, stream model.FileStreamer, up driver.UpdateProgress) error { |
| | var ( |
| | chunks []oss.FileChunk |
| | parts []oss.UploadPart |
| | imur oss.InitiateMultipartUploadResult |
| | ossClient *oss.Client |
| | bucket *oss.Bucket |
| | err error |
| | ) |
| |
|
| | tmpF, err := stream.CacheFullInTempFile() |
| | if err != nil { |
| | return err |
| | } |
| |
|
| | if ossClient, err = oss.New(params.Endpoint, params.AccessKeyID, params.AccessKeySecret); err != nil { |
| | return err |
| | } |
| |
|
| | if bucket, err = ossClient.Bucket(params.Bucket); err != nil { |
| | return err |
| | } |
| |
|
| | ticker := time.NewTicker(time.Hour * 12) |
| | defer ticker.Stop() |
| | |
| | timeout := time.NewTimer(time.Hour * 24) |
| |
|
| | if chunks, err = SplitFile(fileSize); err != nil { |
| | return err |
| | } |
| |
|
| | if imur, err = bucket.InitiateMultipartUpload(params.Key, |
| | oss.SetHeader(OssSecurityTokenHeaderName, params.SecurityToken), |
| | oss.UserAgentHeader(OSSUserAgent), |
| | ); err != nil { |
| | return err |
| | } |
| |
|
| | wg := sync.WaitGroup{} |
| | wg.Add(len(chunks)) |
| |
|
| | chunksCh := make(chan oss.FileChunk) |
| | errCh := make(chan error) |
| | UploadedPartsCh := make(chan oss.UploadPart) |
| | quit := make(chan struct{}) |
| |
|
| | |
| | go chunksProducer(chunksCh, chunks) |
| | go func() { |
| | wg.Wait() |
| | quit <- struct{}{} |
| | }() |
| |
|
| | |
| | for i := 0; i < ThreadsNum; i++ { |
| | go func(threadId int) { |
| | defer func() { |
| | if r := recover(); r != nil { |
| | errCh <- fmt.Errorf("recovered in %v", r) |
| | } |
| | }() |
| | for chunk := range chunksCh { |
| | var part oss.UploadPart |
| | for retry := 0; retry < 3; retry++ { |
| | select { |
| | case <-ticker.C: |
| | errCh <- errors.Wrap(err, "ossToken 过期") |
| | default: |
| | } |
| |
|
| | buf := make([]byte, chunk.Size) |
| | if _, err = tmpF.ReadAt(buf, chunk.Offset); err != nil && !errors.Is(err, io.EOF) { |
| | continue |
| | } |
| |
|
| | b := bytes.NewBuffer(buf) |
| | if part, err = bucket.UploadPart(imur, b, chunk.Size, chunk.Number, OssOption(params)...); err == nil { |
| | break |
| | } |
| | } |
| | if err != nil { |
| | errCh <- errors.Wrap(err, fmt.Sprintf("上传 %s 的第%d个分片时出现错误:%v", stream.GetName(), chunk.Number, err)) |
| | } |
| | UploadedPartsCh <- part |
| | } |
| | }(i) |
| | } |
| |
|
| | go func() { |
| | for part := range UploadedPartsCh { |
| | parts = append(parts, part) |
| | wg.Done() |
| | } |
| | }() |
| | LOOP: |
| | for { |
| | select { |
| | case <-ticker.C: |
| | |
| | return err |
| | case <-quit: |
| | break LOOP |
| | case <-errCh: |
| | return err |
| | case <-timeout.C: |
| | return fmt.Errorf("time out") |
| | } |
| | } |
| |
|
| | |
| | if _, err = bucket.CompleteMultipartUpload(imur, parts, OssOption(params)...); err != nil && !errors.Is(err, io.EOF) { |
| | |
| | if filename := filepath.Base(stream.GetName()); !strings.ContainsAny(filename, "&<") { |
| | return err |
| | } |
| | } |
| | return nil |
| | } |
| |
|
| | func chunksProducer(ch chan oss.FileChunk, chunks []oss.FileChunk) { |
| | for _, chunk := range chunks { |
| | ch <- chunk |
| | } |
| | } |
| |
|
| | func SplitFile(fileSize int64) (chunks []oss.FileChunk, err error) { |
| | for i := int64(1); i < 10; i++ { |
| | if fileSize < i*utils.GB { |
| | if chunks, err = SplitFileByPartNum(fileSize, int(i*100)); err != nil { |
| | return |
| | } |
| | break |
| | } |
| | } |
| | if fileSize > 9*utils.GB { |
| | if chunks, err = SplitFileByPartNum(fileSize, 1000); err != nil { |
| | return |
| | } |
| | } |
| | |
| | if chunks[0].Size < 1*utils.MB { |
| | if chunks, err = SplitFileByPartSize(fileSize, 1*utils.MB); err != nil { |
| | return |
| | } |
| | } |
| | return |
| | } |
| |
|
| | |
| | |
| | func SplitFileByPartNum(fileSize int64, chunkNum int) ([]oss.FileChunk, error) { |
| | if chunkNum <= 0 || chunkNum > 10000 { |
| | return nil, errors.New("chunkNum invalid") |
| | } |
| |
|
| | if int64(chunkNum) > fileSize { |
| | return nil, errors.New("oss: chunkNum invalid") |
| | } |
| |
|
| | var chunks []oss.FileChunk |
| | chunk := oss.FileChunk{} |
| | chunkN := (int64)(chunkNum) |
| | for i := int64(0); i < chunkN; i++ { |
| | chunk.Number = int(i + 1) |
| | chunk.Offset = i * (fileSize / chunkN) |
| | if i == chunkN-1 { |
| | chunk.Size = fileSize/chunkN + fileSize%chunkN |
| | } else { |
| | chunk.Size = fileSize / chunkN |
| | } |
| | chunks = append(chunks, chunk) |
| | } |
| |
|
| | return chunks, nil |
| | } |
| |
|
| | |
| | |
| | func SplitFileByPartSize(fileSize int64, chunkSize int64) ([]oss.FileChunk, error) { |
| | if chunkSize <= 0 { |
| | return nil, errors.New("chunkSize invalid") |
| | } |
| |
|
| | chunkN := fileSize / chunkSize |
| | if chunkN >= 10000 { |
| | return nil, errors.New("Too many parts, please increase part size") |
| | } |
| |
|
| | var chunks []oss.FileChunk |
| | chunk := oss.FileChunk{} |
| | for i := int64(0); i < chunkN; i++ { |
| | chunk.Number = int(i + 1) |
| | chunk.Offset = i * chunkSize |
| | chunk.Size = chunkSize |
| | chunks = append(chunks, chunk) |
| | } |
| |
|
| | if fileSize%chunkSize > 0 { |
| | chunk.Number = len(chunks) + 1 |
| | chunk.Offset = int64(len(chunks)) * chunkSize |
| | chunk.Size = fileSize % chunkSize |
| | chunks = append(chunks, chunk) |
| | } |
| |
|
| | return chunks, nil |
| | } |
| |
|
| | |
| | func OssOption(params *S3Params) []oss.Option { |
| | options := []oss.Option{ |
| | oss.SetHeader(OssSecurityTokenHeaderName, params.SecurityToken), |
| | oss.UserAgentHeader(OSSUserAgent), |
| | } |
| | return options |
| | } |
| |
|