This commit is contained in:
Wolves 2023-07-27 21:18:45 +08:00
commit 3287531ea5
14 changed files with 521 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.env
**/.DS_Store
.DS_Store?

6
README.md Normal file
View File

@ -0,0 +1,6 @@
# 百度云盘上传api
- [api文档](https://pan.baidu.com/union/doc/3ksg0s9ye)
- demo/upload_precreate.go 为如何使用 precreate 预上传接口的示例。
- demo/upload_upload.go 为如何使用 upload 分片上传接口的示例。
- demo/upload_create.go 为如何使用 create 创建文件接口的示例。

BIN
demo/datademo/testfile.pdf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

38
demo/upload_create.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"fmt"
"gitea.wolves.top/golang/BaiduDisk-util/upload"
)
func main() {
// 使用示例
// 用户的access_token
accessToken := "your-access-token"
// path与precreate的一致
path := "/apps/hhhkoo/testfile.pdf"
// size与precreate的一致
var size uint64
size = 7141246
// blocklist与precreate的一致
blockList := []string{}
blockList = append(blockList, "18f2607ce63714753ef640be507fe8f4")
blockList = append(blockList, "f4bea7d615c2429258f615397c7cd11c")
// uploadId为precreate返回的uploadId
uploadId := "P1-MTAuMTQ0LjEyOC4xNToxNjUxMDI5MTE1OjM5NTM0NjY0MTQ4NjIxMzE0OA=="
// // call create API
arg := upload.NewCreateArg(uploadId, path, size, blockList)
if ret, err := upload.Create(accessToken, arg); err != nil {
fmt.Printf("[msg: create this part error] [err:%v]", err.Error())
} else {
fmt.Printf("ret:%+v", ret)
}
}

38
demo/upload_precreate.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"fmt"
"gitea.wolves.top/golang/BaiduDisk-util/upload"
)
func main() {
// 使用示例
// 用户的access_token
accessToken := "your-access-token"
// 要保存到网盘的文件路径
// 前缀必须为:/apps/您的应用名/
path := "/apps/hhhkoo/testfile.pdf"
// 测试文件testfile.pdf的大小单位B
var size uint64
size = 7141246
// testfile.pdf 6.8MB超出4MB。以4MB为单位切分切分为两个aa和ab。
// 分片aa 的md5 为18f2607ce63714753ef640be507fe8f4
// 分片ab 的md5 为f4bea7d615c2429258f615397c7cd11c
// MD5是一种通用的加密算法
// shell语言下获取文件aa MD5的语法是 md5sum aa
var blockList []string
blockList = append(blockList, "18f2607ce63714753ef640be507fe8f4")
blockList = append(blockList, "f4bea7d615c2429258f615397c7cd11c")
// call precreate API
arg := upload.NewPrecreateArg(path, size, blockList)
if ret, err := upload.Precreate(accessToken, arg); err != nil {
fmt.Printf("[msg: precreate error] [err:%v]", err.Error())
} else {
fmt.Printf("ret:%+v", ret)
}
}

42
demo/upload_upload.go Normal file
View File

@ -0,0 +1,42 @@
package main
import (
"fmt"
"gitea.wolves.top/golang/BaiduDisk-util/upload"
)
func main() {
// 使用示例
// 用户的access_token
accessToken := "your-access-token"
// uploadId为precreate返回的uploadId
uploadId := "P1-MTAuMTQ0LjEyOC4xNToxNjUxMDI5MTE1OjM5NTM0NjY0MTQ4NjIxMzE0OA=="
// path与precreate的一致
path := "/apps/hhhkoo/testfile.pdf"
// 上传第1个分片
file := "第1个分片aa的本地文件路径"
partseq := 0
// call upload API
arg := upload.NewUploadArg(uploadId, path, file, partseq)
if ret, err := upload.Upload(accessToken, arg); err != nil {
fmt.Printf("[msg: upload this part error] [err:%v]", err.Error())
} else {
fmt.Printf("ret:%+v", ret)
}
// 上传第2个分片
file = "第2个分片ab的本地文件路径"
partseq = 1
// call upload API
arg = upload.NewUploadArg(uploadId, path, file, partseq)
if ret, err := upload.Upload(accessToken, arg); err != nil {
fmt.Printf("[msg: upload this part error] [err:%v]", err.Error())
} else {
fmt.Printf("ret:%+v", ret)
}
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module gitea.wolves.top/golang/BaiduDisk-util
go 1.20

58
upload/create.go Normal file
View File

@ -0,0 +1,58 @@
package upload
import (
"encoding/json"
"errors"
"gitea.wolves.top/golang/BaiduDisk-util/utils"
"net/url"
"strconv"
"strings"
)
// create
//
// RETURNS:
// - CreateReturn: create return
// - error: the return error if any occurs
func Create(accessToken string, arg *CreateArg) (CreateReturn, error) {
ret := CreateReturn{}
protocal := "https"
host := "pan.baidu.com"
router := "/rest/2.0/xpan/file?method=create&"
uri := protocal + "://" + host + router
headers := map[string]string{
"Host": host,
"Content-Type": "application/x-www-form-urlencoded",
}
params := url.Values{}
params.Set("access_token", accessToken)
uri += params.Encode()
postBody := url.Values{}
postBody.Add("rtype", "2")
postBody.Add("path", arg.Path)
postBody.Add("size", strconv.FormatUint(arg.Size, 10))
postBody.Add("isdir", "0")
js, _ := json.Marshal(arg.BlockList)
postBody.Add("block_list", string(js))
postBody.Add("uploadid", arg.UploadId)
var body string
var err error
body, _, err = utils.DoHTTPRequest(uri, strings.NewReader(postBody.Encode()), headers)
if err != nil {
return ret, err
}
if err = json.Unmarshal([]byte(body), &ret); err != nil {
return ret, errors.New("unmarshal create body failed")
}
if ret.Errno != 0 {
return ret, errors.New("call create failed")
}
return ret, nil
}

55
upload/precreate.go Normal file
View File

@ -0,0 +1,55 @@
package upload
import (
"encoding/json"
"errors"
"gitea.wolves.top/golang/BaiduDisk-util/utils"
"net/url"
"strconv"
"strings"
)
// Precreate
//
// RETURNS:
// - PrecreateReturn: precreate return
// - error: the return error if any occurs
func Precreate(accessToken string, arg *PrecreateArg) (PrecreateReturn, error) {
ret := PrecreateReturn{}
protocal := "https"
host := "pan.baidu.com"
router := "/rest/2.0/xpan/file?method=precreate&"
uri := protocal + "://" + host + router
params := url.Values{}
params.Set("access_token", accessToken)
uri += params.Encode()
headers := map[string]string{
"Host": host,
"Content-Type": "application/x-www-form-urlencoded",
}
postBody := url.Values{}
postBody.Add("path", arg.Path)
postBody.Add("size", strconv.FormatUint(arg.Size, 10))
blockListJson, _ := json.Marshal(arg.BlockList)
postBody.Add("block_list", string(blockListJson))
postBody.Add("isdir", "0")
postBody.Add("autoinit", "1")
// 当path冲突且block_list不同时进行重命名
postBody.Add("rtype", "2")
body, _, err := utils.DoHTTPRequest(uri, strings.NewReader(postBody.Encode()), headers)
if err != nil {
return ret, err
}
if err = json.Unmarshal([]byte(body), &ret); err != nil {
return ret, errors.New("unmarshal precreate body failed,body")
}
if ret.Errno != 0 {
return ret, errors.New("call precreate failed")
}
return ret, nil
}

74
upload/types.go Normal file
View File

@ -0,0 +1,74 @@
package upload
// precreate 参数
type PrecreateArg struct {
Path string `json:"path"`
Size uint64 `json:"size"`
BlockList []string `json:"block_list"`
}
// 创建 PrecreateArg 实例
func NewPrecreateArg(path string, size uint64, blockList []string) *PrecreateArg {
s := new(PrecreateArg)
s.Path = path
s.Size = size
s.BlockList = blockList
return s
}
// PrecreateReturn
type PrecreateReturn struct {
Errno int `json:"errno"`
ReturnType int `json:"return_type"`
BlockList []int `json:"block_list"` // 分片序号列表
UploadId string `json:"uploadid"`
RequestId int `json:"request_id"`
}
// upload 参数
type UploadArg struct {
UploadId string `json:"uploadid"`
Path string `json:"path"`
LocalFile string `json:"local_file"`
Partseq int `json:"partseq"`
}
// 创建 UploadArg 实例
func NewUploadArg(uploadId string, path string, localFile string, partseq int) *UploadArg {
s := new(UploadArg)
s.UploadId = uploadId
s.Path = path
s.LocalFile = localFile
s.Partseq = partseq
return s
}
// UploadReturn
type UploadReturn struct {
Md5 string `json:"md5"`
RequestId int `json:"request_id"`
}
// create 参数
type CreateArg struct {
UploadId string `json:"uploadid"`
Path string `json:"path"`
Size uint64 `json:"size"`
BlockList []string `json:"block_list"`
}
// 创建 CreateArg 实例
func NewCreateArg(uploadId string, path string, size uint64, blockList []string) *CreateArg {
s := new(CreateArg)
s.UploadId = uploadId
s.Path = path
s.Size = size
s.BlockList = blockList
return s
}
// CreateReturn
type CreateReturn struct {
Errno int `json:"errno"`
Path string `json:"path"`
}

86
upload/upload.go Normal file
View File

@ -0,0 +1,86 @@
package upload
import (
"bytes"
"encoding/json"
"errors"
"gitea.wolves.top/golang/BaiduDisk-util/utils"
"io"
"mime/multipart"
"net/url"
"os"
"strconv"
)
func Upload(accessToken string, arg *UploadArg) (UploadReturn, error) {
ret := UploadReturn{}
//打开文件句柄操作
fileHandle, err := os.Open(arg.LocalFile)
if err != nil {
return ret, errors.New("superfile2 open file failed")
}
defer fileHandle.Close()
// 获取文件当前信息
fileInfo, err := fileHandle.Stat()
if err != nil {
return ret, err
}
// 读取文件块
buf := make([]byte, fileInfo.Size())
n, err := fileHandle.Read(buf)
if err != nil {
if err != io.EOF {
return ret, err
}
}
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, err := bodyWriter.CreateFormFile("file", "file")
if err != nil {
return ret, err
}
//iocopy
_, err = io.Copy(fileWriter, bytes.NewReader(buf[0:n]))
if err != nil {
return ret, err
}
bodyWriter.Close()
protocal := "https"
host := "d.pcs.baidu.com"
router := "/rest/2.0/pcs/superfile2?method=upload&"
uri := protocal + "://" + host + router
params := url.Values{}
params.Set("access_token", accessToken)
params.Set("path", arg.Path)
params.Set("uploadid", arg.UploadId)
params.Set("partseq", strconv.Itoa(arg.Partseq))
uri += params.Encode()
contentType := bodyWriter.FormDataContentType()
headers := map[string]string{
"Host": host,
"Content-Type": contentType,
}
body, _, err := utils.SendHTTPRequest(uri, bodyBuf, headers)
if err != nil {
return ret, err
}
if err := json.Unmarshal([]byte(body), &ret); err != nil {
return ret, errors.New("unmarshal body failed")
}
if ret.Md5 == "" {
return ret, errors.New("md5 is empty")
}
return ret, nil
}

93
utils/util.go Normal file
View File

@ -0,0 +1,93 @@
package utils
import (
"bytes"
"crypto/tls"
"io"
"io/ioutil"
"net/http"
"time"
)
type Request struct {
Host string
Route string
QueryArg interface{}
Body interface{}
Headers map[string]string
}
func DoHTTPRequest(url string, body io.Reader, headers map[string]string) (string, int, error) {
timeout := 5 * time.Second
retryTimes := 3
tr := &http.Transport{
MaxIdleConnsPerHost: -1,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
httpClient := &http.Client{Transport: tr}
httpClient.Timeout = timeout
req, err := http.NewRequest("POST", url, body)
if err != nil {
return "", 0, err
}
// request header
for k, v := range headers {
req.Header.Add(k, v)
}
var resp *http.Response
for i := 1; i <= retryTimes; i++ {
resp, err = httpClient.Do(req)
if err == nil {
break
}
if i == retryTimes {
return "", 0, err
}
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", resp.StatusCode, err
}
return string(respBody), resp.StatusCode, nil
}
// for superfile2
func SendHTTPRequest(url string, body io.Reader, headers map[string]string) (string, int, error) {
timeout := 60 * time.Second
retryTimes := 3
postData, _ := ioutil.ReadAll(body)
var resp *http.Response
for i := 1; i <= retryTimes; i++ {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
MaxIdleConnsPerHost: -1,
}
httpClient := &http.Client{Transport: tr}
httpClient.Timeout = timeout
req, err := http.NewRequest("POST", url, bytes.NewBuffer(postData))
if err != nil {
return "", 0, err
}
// request header
for k, v := range headers {
req.Header.Add(k, v)
}
resp, err = httpClient.Do(req)
if err == nil {
break
}
if i == retryTimes {
return "", 0, err
}
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", resp.StatusCode, err
}
return string(respBody), resp.StatusCode, nil
}