防微杜漸,未雨綢繆,百度網(wǎng)盤(百度云盤)接口API自動(dòng)化備份上傳以及開源發(fā)布,基于Golang1.18

奉行長(zhǎng)期主義的開發(fā)者都有一個(gè)共識(shí):對(duì)于服務(wù)器來說程拭,數(shù)據(jù)備份非常重要,因?yàn)榉?wù)器上的數(shù)據(jù)通常是無價(jià)的棍潘,如果丟失了這些數(shù)據(jù)恃鞋,可能會(huì)導(dǎo)致嚴(yán)重的后果崖媚,伴隨云時(shí)代的發(fā)展,備份技術(shù)也讓千行百業(yè)看到了其“云基因”的成長(zhǎng)與進(jìn)化恤浪,即基于云存儲(chǔ)的云備份畅哑。

本次我們使用Golang1.18完成百度網(wǎng)盤(百度云盤)接口API自動(dòng)化備份上傳功能,以及演示如何將該模塊進(jìn)行開源發(fā)布水由。

百度網(wǎng)盤API接入授權(quán)

如果希望golang服務(wù)可以訪問并且上傳用戶的百度網(wǎng)盤拐邪,則需要經(jīng)過用戶同意厘唾,這個(gè)流程被稱為“授權(quán)”。百度網(wǎng)盤開放平臺(tái)基于 OAuth2.0 接入授權(quán)。OAuth2.0 是一種授權(quán)協(xié)議宰掉,通過該協(xié)議用戶可以授權(quán)開發(fā)者應(yīng)用訪問個(gè)人網(wǎng)盤信息與文件补君。

用戶同意授權(quán)后矿辽,開發(fā)者應(yīng)用會(huì)獲取到一個(gè) Access Token竞膳,該 Access Token 是用戶同意授權(quán)的憑證。開發(fā)者應(yīng)用需要依賴 Access Token 憑證調(diào)用百度網(wǎng)盤公開API彤恶,實(shí)現(xiàn)訪問用戶網(wǎng)盤信息與授權(quán)資源钞钙。

基本流程和三方登錄差不多,需要跳轉(zhuǎn)百度網(wǎng)盤授權(quán)頁進(jìn)行授權(quán)動(dòng)作声离,隨后授權(quán)碼(code)會(huì)發(fā)送到回調(diào)網(wǎng)址芒炼,再用授權(quán)碼換取Access Token。但不一樣的是抵恋,百度官網(wǎng)提供一種相對(duì)簡(jiǎn)單的獲取code方式焕议,即oob,所謂oob就是直接在線請(qǐng)求后在表單中復(fù)制授權(quán)碼即可弧关,不需要回調(diào)網(wǎng)址的參與。

首先根據(jù)官網(wǎng)文檔:https://pan.baidu.com/union/doc/ol0rsap9s 創(chuàng)建應(yīng)用唤锉,創(chuàng)建好之后世囊,將應(yīng)用id拼接位oob授權(quán)網(wǎng)址:

https://openapi.baidu.com/oauth/2.0/authorize?client_id=你的應(yīng)用id&response_type=code&redirect_uri=oob&scope=basic+netdisk

在線訪問復(fù)制授權(quán)碼:

image

注意授權(quán)碼一次性有效并且會(huì)在10分鐘后過期,隨后編寫代碼獲取token:

package bdyp  
  
import (  
    "fmt"  
    "net/http"  
    "net/url"  
)  
  
type Bcloud struct {  
    app_key      string  
    app_secret   string  
    accessToken  string  
    refreshToken string  
    logger       Logger  
}  
  
type tokenResp struct {  
    *Token  
    ErrorDescription string `json:"error_description"`  
}  
  
type Token struct {  
    AccessToken  string `json:"access_token"`  
    RefreshToken string `json:"refresh_token"`  
    ExpiresIn    int    `json:"expires_in"`  
}  
  
func (r *Bcloud) GetToken(code, redirectURI, app_key, app_secret string) (*Token, error) {  
    uri := fmt.Sprintf("https://openapi.baidu.com/oauth/2.0/token?"+  
        "grant_type=authorization_code&"+  
        "code=%s&"+  
        "client_id=%s&"+  
        "client_secret=%s&"+  
        "redirect_uri=%s",  
        url.QueryEscape(code),  
        url.QueryEscape(app_key),  
        url.QueryEscape(app_secret),  
        redirectURI)  
    resp := new(tokenResp)  
  
    err := r.requestJSON(http.MethodGet, uri, nil, resp)  
    if err != nil {  
        return nil, err  
    } else if resp.ErrorDescription != "" {  
        return nil, fmt.Errorf(resp.ErrorDescription)  
    }  
  
    r.app_key = app_key  
    r.app_secret = app_secret  
    r.accessToken = resp.AccessToken  
    r.refreshToken = resp.RefreshToken  
  
    return resp.Token, nil  
}

這里分別創(chuàng)建網(wǎng)盤結(jié)構(gòu)體和秘鑰結(jié)構(gòu)體窿祥,通過官方接口將oob方式獲取的code交換token株憾,分別為accessToken和refreshToken,refreshToken用于刷新 Access Token, 有效期為10年晒衩。

這里最好將token寫入文件或者存入數(shù)據(jù)庫嗤瞎,本文只討論授權(quán)和上傳邏輯,故不加入數(shù)據(jù)庫的相關(guān)操作听系。

至此贝奇,百度網(wǎng)盤的授權(quán)操作就完成了。

服務(wù)器本地文件上傳至百度網(wǎng)盤

根據(jù)官網(wǎng)文檔描述:https://pan.baidu.com/union/doc/3ksg0s9ye靠胜,上傳流程是指掉瞳,用戶將本地文件上傳到百度網(wǎng)盤云端服務(wù)器的過程毕源。文件上傳分為三個(gè)階段:預(yù)上傳、分片上傳陕习、創(chuàng)建文件霎褐。第二個(gè)階段分片上傳依賴第一個(gè)階段預(yù)上傳的結(jié)果,第三個(gè)階段創(chuàng)建文件依賴第一個(gè)階段預(yù)上傳和第二階段分片上傳的結(jié)果该镣,串行完成這三個(gè)階段任務(wù)后冻璃,本地文件成功上傳到網(wǎng)盤服務(wù)器。

說白了损合,有點(diǎn)像HTTP連接的三次握手省艳,目的就是為了保證上傳數(shù)據(jù)的完整性,強(qiáng)制串行的原子操作也有利于保證上傳任務(wù)的可靠性塌忽。

首先構(gòu)建預(yù)上傳函數(shù):

func (r *Bcloud) FileUploadSessionStart(req *FileUploadSessionStartReq) (*FileUploadSessionStartResp, error) {  
    token, err := r.getAuthToken()  
    if err != nil {  
        return nil, err  
    }  
  
    req.Method = "precreate"  
    req.AccessToken = token  
  
    req_, err := req.to()  
    if err != nil {  
        return nil, err  
    }  
  
    resp := new(FileUploadSessionStartResp)  
  
    err = r.requestURLEncode(http.MethodPost, "https://pan.baidu.com/rest/2.0/xpan/file", req_, resp)  
    if err != nil {  
        return nil, err  
    } else if err := resp.Err(); err != nil {  
        return nil, err  
    }  
  
    if len(resp.BlockList) == 0 {  
        resp.BlockList = []int64{0}  
    }  
  
    return resp, nil  
}

這里參數(shù)為預(yù)上傳參數(shù)的結(jié)構(gòu)體:

type FileUploadSessionStartReq struct {  
    Method      string `query:"method"`  
    AccessToken string `query:"access_token"`  
    Path        string `json:"path"`  
    File        io.Reader  
    RType       *int64 `json:"rtype"`  
}

隨后是分片上傳邏輯:

func (r *Bcloud) FileUploadSessionAppend(req *FileUploadSessionAppendReq) error {  
    token, err := r.getAuthToken()  
    if err != nil {  
        return err  
    }  
  
    req.Method = "upload"  
    req.AccessToken = token  
    req.Type = "tmpfile"  
  
    resp := new(fileUploadSessionAppendResp)  
  
    err = r.requestForm(http.MethodPost, "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2", req, resp)  
    if err != nil {  
        return err  
    } else if err := resp.Err(); err != nil {  
        return err  
    } else if resp.ErrorMsg != "" {  
        return fmt.Errorf(resp.ErrorMsg)  
    }  
  
    return nil  
}  
  
type FileUploadSessionAppendReq struct {  
    Method      string    `query:"method"` // 本接口固定為precreate  
    AccessToken string    `query:"access_token"`  
    Type        string    `query:"type"`     // 固定值 tmpfile  
    Path        string    `query:"path"`     // 需要與上一個(gè)階段預(yù)上傳precreate接口中的path保持一致  
    UploadID    string    `query:"uploadid"` // 上一個(gè)階段預(yù)上傳precreate接口下發(fā)的uploadid  
    PartSeq     int64     `query:"partseq"`  // 文件分片的位置序號(hào)拍埠,從0開始,參考上一個(gè)階段預(yù)上傳precreate接口返回的block_list  
    File        io.Reader `file:"file"`      // 是       RequestBody參數(shù)   上傳的文件內(nèi)容  
}

對(duì)于總體積大于4mb的文件土居,通過切片的方式進(jìn)行上傳枣购。

總后是合并文件寫入文件邏輯:

func (r *Bcloud) FileUploadSessionFinish(req *FileUploadSessionFinishReq) error {  
    token, err := r.getAuthToken()  
    if err != nil {  
        return err  
    }  
  
    req.Method = "create"  
    req.AccessToken = token  
  
    req_, err := req.to()  
    if err != nil {  
        return err  
    }  
  
    resp := new(fileUploadSessionFinishResp)  
  
    err = r.requestURLEncode(http.MethodPost, "https://pan.baidu.com/rest/2.0/xpan/file", req_, resp)  
    if err != nil {  
        return err  
    } else if err := resp.Err(); err != nil {  
        return err  
    }  
  
    return nil  
}  
  
type FileUploadSessionFinishReq struct {  
    Method      string    `query:"method"`  
    AccessToken string    `query:"access_token"`  
    Path        string    `json:"path"`  
    File        io.Reader `json:"-"`  
    UploadID    string    `json:"uploadid"`  
    RType       *int64    `json:"rtype"`  
}

至此,完成了文件上傳的三個(gè)階段:預(yù)上傳擦耀、分片上傳棉圈、創(chuàng)建文件。

開源發(fā)布Publish

我們知道在 Golang的項(xiàng)目中眷蜓,可以 import 一個(gè)托管在遠(yuǎn)程倉(cāng)庫的模塊分瘾,這個(gè)模塊在我們使用 go get 的時(shí)候,會(huì)下載到本地吁系。既然是放在遠(yuǎn)程倉(cāng)庫上德召,意味著所有人都可以發(fā)布,并且所以人也都可以使用汽纤,所以為了讓鄉(xiāng)親們更方便地上傳數(shù)據(jù)到百度網(wǎng)盤上岗,讓我們把這個(gè)項(xiàng)目開源。

先在你的 Github 上新建一個(gè)倉(cāng)庫蕴坪,記得選 Public(公開項(xiàng)目)肴掷,隨后將項(xiàng)目代碼推送到Github上面:

echo "# bdyp_upload_golang" >> README.md  
git init  
git add README.md  
git commit -m "first commit"  
git branch -M main  
git remote add origin https://github.com/zcxey2911/bdyp_upload_golang.git  
git push -u origin main

在項(xiàng)目根目錄使用go mod init 命令進(jìn)行初始化,注意這里的模塊名背传,填寫我們的git倉(cāng)庫名稱呆瞻,但是不要帶著.git:

go mod init github.com/zcxey2911/bdyp_upload_golang

再次推送項(xiàng)目模塊代碼:

git add -A  
git commit -m "Add a go mod file"
git push -u origin main

全部完成以后,刷新我們的倉(cāng)庫径玖,就可以看到我們的剛剛上傳的項(xiàng)目代碼了痴脾,點(diǎn)擊 release 發(fā)布一個(gè)版本即可。

最后挺狰,通過go get命令安裝發(fā)布之后的模塊:

go get github.com/zcxey2911/bdyp_upload_golang

完整的調(diào)用流程:

package main  
  
import (  
    "fmt"  
    bdyp "github.com/zcxey2911/bdyp_upload_golang"  
    "os"  
)  
  
func main() {  
  
    var bcloud = bdyp.Bcloud{}  
  
    // 獲取token  
    res, err := bcloud.GetToken("oob獲取的code", "oob", "應(yīng)用appkey", "應(yīng)用appsecret")  
  
    fmt.Println(res)  
  
    if err != nil {  
        fmt.Println("err", err)  
    } else {  
        fmt.Printf("接口的token是: %#v\n", res.AccessToken)  
    }  
    // 讀取文件  
    f, err := os.Open("/Users/liuyue/Downloads/ju1.webp")  
    if err != nil {  
        fmt.Println("err", err)  
        return  
    }  
    defer f.Close()  
  
    // 上傳文件  
    print(bcloud.Upload(&bdyp.FileUploadReq{  
        Name:  "/apps/云盤備份/ju2.webp",  
        File:  f,  
        RType: nil,  
    }))  
  
}

查看上傳的數(shù)據(jù):

image

簡(jiǎn)單快速明郭,一氣呵成买窟。

結(jié)語

當(dāng)然了百度云盤備份也不是沒有缺陷,將數(shù)據(jù)存儲(chǔ)在云端可能會(huì)存在安全性和隱私性問題薯定,與此同時(shí)始绍,數(shù)據(jù)量很大或者數(shù)據(jù)分布在不同地點(diǎn)的情況下,恢復(fù)數(shù)據(jù)所需的時(shí)間會(huì)比較長(zhǎng)话侄。不差錢的同學(xué)也可以選擇磁盤快照服務(wù)亏推,最后奉上項(xiàng)目地址,與君共勉:https://github.com/zcxey2911/bdyp_upload_golang

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末年堆,一起剝皮案震驚了整個(gè)濱河市吞杭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌变丧,老刑警劉巖芽狗,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異痒蓬,居然都是意外死亡童擎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門攻晒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顾复,“玉大人,你說我怎么就攤上這事鲁捏⌒驹遥” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵给梅,是天一觀的道長(zhǎng)假丧。 經(jīng)常有香客問我,道長(zhǎng)动羽,這世上最難降的妖魔是什么虎谢? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮曹质,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘擎场。我一直安慰自己羽德,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布迅办。 她就那樣靜靜地躺著宅静,像睡著了一般。 火紅的嫁衣襯著肌膚如雪站欺。 梳的紋絲不亂的頭發(fā)上姨夹,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天纤垂,我揣著相機(jī)與錄音,去河邊找鬼磷账。 笑死峭沦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逃糟。 我是一名探鬼主播吼鱼,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼绰咽!你這毒婦竟也來了菇肃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤取募,失蹤者是張志新(化名)和其女友劉穎琐谤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玩敏,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斗忌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了聊品。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片飞蹂。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖翻屈,靈堂內(nèi)的尸體忽然破棺而出陈哑,到底是詐尸還是另有隱情,我是刑警寧澤伸眶,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布惊窖,位于F島的核電站,受9級(jí)特大地震影響厘贼,放射性物質(zhì)發(fā)生泄漏界酒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一嘴秸、第九天 我趴在偏房一處隱蔽的房頂上張望毁欣。 院中可真熱鬧,春花似錦岳掐、人聲如沸凭疮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽执解。三九已至,卻和暖如春纲酗,著一層夾襖步出監(jiān)牢的瞬間衰腌,已是汗流浹背新蟆。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留右蕊,地道東北人琼稻。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像尤泽,于是被迫代替她去往敵國(guó)和親欣簇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容