今天突然有个需求要搭建一个在线资源文件管理平台,发现Bigfile可以用来做后端服务,Bigfile
是使用golang开发的一个文件传输管理系统。
如何创建APP,启动http服务请参考使用文档:https://learnku.com/docs/bigfile/1.0 。
当启动好http服务后,第一步创建token,创建 Token
是接下来所有操作的开始,让我们先开看一下 API 参数。
POST /api/bigfile/token/create
name | type | required | description |
---|---|---|---|
appUid | string | yes | APP UID |
nonce | string | yes | 32 到 48 长度的随机字符串 |
sign | string | yes | 请求参数签名 |
path | string | no | Token 的作用域,默认为:/ ,可以理解为根目录 |
ip | string | no | 限制当前 Token 仅能被使用的 IP 列表,多个 IP 逗号分隔 |
expiredAt | timestamp | no | Token 失效时间,默认永久有效 |
secret | string | no | Token 密钥,12 到 32 位,建议设置,默认为空 |
availableTimes | int | no | Token 可用次数,默认无限次数 |
readOnly | bool | no | 限制此 Token 仅能被用于去下载文件 |
若启动服务使用了证书文件像下面方式启动:
bigfile http:start --cert-file server.pem --cert-key server.key
那么使用官方文档的例子可能出现如下错误:
x509: certificate signed by unknown authority
是因为使用 rpc:make-cert
生成的证书不被系统信任,这里有三种解决方案:
我使用的是第三种方式,具体代码如下:
创建一个tools.go文件包名为utils
package utils
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
"strings"
)
//封装返回一个*http.Client
func client(rootCa, rootKey string) *http.Client {
var tr *http.Transport
certs, err := tls.LoadX509KeyPair(rootCa, rootKey)
if err != nil {
fmt.Println("未找到证书文件")
tr = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
} else {
ca, err := x509.ParseCertificate(certs.Certificate[0])
if err != nil {
return &http.Client{Transport: tr}
}
pool := x509.NewCertPool()
pool.AddCert(ca)
tr = &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool},
}
}
return &http.Client{Transport: tr}
}
//发送post请求
func WPost(url, rootCa, rootKey ,body string ) ([]byte, error) {
resp, err := client(rootCa, rootKey).Post(url,"application/x-www-form-urlencoded",strings.NewReader(body))
if err != nil {
return nil, err
}
defer resp.Body.Close()
b,err:=ioutil.ReadAll(resp.Body)
if err!=nil{
return nil,err
}
return b, nil
}
//封装Request包发送,用于后面上传文件
func WDo(rootCa,rootKey string,request *http.Request) ([]byte, error){
resp,err:=client(rootCa,rootKey).Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b,err:=ioutil.ReadAll(resp.Body)
if err!=nil{
return nil,err
}
return b, nil
}
创建实现方法文件function.go
package utils
import (
"bytes"
"fmt"
"github.com/bigfile/bigfile/databases/models"
"github.com/bigfile/bigfile/http"
"io"
"log"
"mime/multipart"
libHttp "net/http"
"os"
"time"
)
//MakeToken 创建token
func MakeToken(){
appUid := "d972584eddc59247c322ca8b38782065"
appSecret := "2VjGpzrmJQiJ"
body := http.GetParamsSignBody(map[string]interface{}{
"appUid": appUid,
"nonce": models.RandomWithMD5(128),
"path": "/images/png",
"expiredAt": time.Now().AddDate(0, 0, 2).Unix(),
"secret": models.RandomWithMD5(44),
"availableTimes": -1,
"readOnly": false,
}, appSecret)
b,e:= WPost(
"https://127.0.0.1:10985/api/bigfile/token/create",
"server/server.pem",//你自己的.pem文件路径
"server/server.key",//你自己的.key文件路径
body)
if e!=nil{
log.Fatal(e)
}
fmt.Println(string(b))
}
//UpFile 上传文件
func UpFile(){
token := "399f28a3c964e137536cda4517c5eaa6"
tokenSecret := "be3041191baad445a0a4c852334b1c2e"
file, err := os.Open("./client/img/timg.jpg")//要上传的文件路径
if err != nil {
fmt.Println(err)
return
}
for index := 0; ; index++ {
var (
err error
body = new(bytes.Buffer)
chunk = make([]byte, models.ChunkSize)
request *libHttp.Request
readCount int
formBodyWriter = multipart.NewWriter(body)
formFileWriter io.Writer
)
// 分片读取,每次读取 1MB
if readCount, err = file.Read(chunk); err != nil {
fmt.Println(err)
return
}
params := map[string]interface{}{
"token": token,
"path": "/profile/timg.jpg",//上传保存的文件路径
"nonce": models.RandomWithMD5(255),
}
if index == 0 {
params["overwrite"] = "1"
} else {
params["append"] = "1"
}
// 签名参数
params["sign"] = http.GetParamsSignature(params, tokenSecret)
for k, v := range params {
if err = formBodyWriter.WriteField(k, v.(string)); err != nil {
fmt.Println(err)
return
}
}
// 写入 request body
if formFileWriter, err = formBodyWriter.CreateFormFile("file", "random.bytes"); err != nil {
fmt.Println(err)
return
}
if _, err = formFileWriter.Write(chunk[:readCount]); err != nil {
fmt.Println(err)
return
}
if err = formBodyWriter.Close(); err != nil {
fmt.Println(err)
return
}
api := "https://127.0.0.1:10985/api/bigfile/file/create"
if request, err = libHttp.NewRequest(libHttp.MethodPost, api, body); err != nil {
fmt.Println(err)
return
}
// 设置请求头
request.Header.Set("Content-Type", formBodyWriter.FormDataContentType())
resp,err:=WDo("server/server.pem",//证书文件路径
"server/server.key",//证书文件路径
request)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(resp))
}
}
//CreateFileLink 创建文件下载链接
func CreateFileLink(token,fileUid,tokenSecret string,openInBrowser bool) string{
body := http.GetParamsSignBody(map[string]interface{}{
"token": token,
"fileUid": fileUid,
"nonce": models.RandomWithMD5(255),
"openInBrowser":true,
}, tokenSecret)
return "https://127.0.0.1:10985/api/bigfile/file/read?"+body
}
创建main.go
func main() {
//创建token
utils.MakeToken()
//上传文件
utils.UpFile()
//获取文件下载地址
fmt.Println(utils.CreateFileLink("399f28a3c964e137536cda4517c5eaa6",
"fdc8a68d7e2578ba201467d65653305d",
"be3041191baad445a0a4c852334b1c2e",
true))
}
//创建token
utils.MakeToken()
返回以下结果:
{
"requestId": 10033,
"success": true,
"errors": null,
"data": {
"availableTimes": -1,
"expiredAt": 1575731179,
"ip": null,
"path": "/images/png",
"readOnly": 0,
"secret": "75d45a4e2b388f302282d2e034456bed",
"token": "d79f17581056b889e3b872ea679a784d"
}
}
//上传文件
utils.UpFile()
上传文件返回如下结果:
{
"requestId": 10034,
"success": true,
"errors": null,
"data": {
"ext": "jpg",
"fileUid": "e3098acd4b62196abca649b64fb6415c",
"hash": "d57b252a571319793bf38a313b88e2ae65e73e8d444ae1eb3224b9e72a121f5b",
"hidden": 0,
"isDir": 0,
"path": "/images/png/profile/timg.jpg",
"size": 88428
}
}
//获取文件下载地址
fmt.Println(utils.CreateFileLink("399f28a3c964e137536cda4517c5eaa6",
"fdc8a68d7e2578ba201467d65653305d",
"be3041191baad445a0a4c852334b1c2e",
true))
获取文件下载链接返回一个地址,可以直接再浏览器上打开:
https://127.0.0.1:10985/api/bigfile/file/read?fileUid=fdc8a68d7e2578ba201467d65653305d&nonce=f142f5c160e4563c118a46d1c6315a18&openInBrowser=true&token=399f28a3c964e137536cda4517c5eaa6&sign=088d73b3d87c3e15ac66802ee368fd91
其它操作类似,不作描述。注意工具类中使用了Bigfile的一下现成的方法,所以需要引用Bigfile的包,若用go mod 代码如下:
module bigfile
go 1.13
require github.com/bigfile/bigfile v1.0.10
然后 go mod tidy加载包。