3.7. 创建 File

未匹配的标注

Github: github.com/bigfile/bigfile

基本概要

如果非要将 Bigfile 中的 API 进行分类,可以分为两类,一类使用 APP 密钥签名,一类使用 Token 密钥签名,如果 Token 没有密钥,那就不用签名了。

需要注意的是,在 Bigfile 中,文件和目录我们都统称为 File,所以 创建 File 既可以用来上传文件,也可以用来创建目录,而且您必须创建一个 Token 才能操作这些文件和目录。由于 Token 是有作用域的,所以一个 Token 只能在它的作用域下进行操作。

API

我们来看一下 API 的基本信息:

POST /api/bigfile/file/create

名称 类型 必填 描述
token string yes token 值
nonce string yes 3248 长度的随机字符串
path string yes 文件存储路径,相对于 Token 路径
file byte no 文件内容,不参与签名,最大:1MB
sign string no 参数签名,如果 Token 是有密钥的,那么它是必填的
hash string no 上传的当前文件的 hash 值,基于 sha256 算法,用于校验,可不填
size int no 上传的当前文件的 size ,单位:byte,用于校验,可不填
overwrite bool no 如果指定的路径存在文件,覆盖
rename bool no 如果指定的路径存在文件,重命名
append bool no 如果指定的路径存在文件,追加,常用于大文件上传
hidden bool no 将文件置为隐藏,被隐藏的文件无法被下载

APIContent-Type 类型必须是:multipart/form-data 类型。

创建文件示例

package main

import (
    "bytes"
    "fmt"
    "io"
    "io/ioutil"
    "mime/multipart"
    libHttp "net/http"
    "os"

    "github.com/bigfile/bigfile/databases/models"
    "github.com/bigfile/bigfile/http"
)

func main() {
    token := "49f92acd696260abba1bc4062d157199"
    tokenSecret := "9bcac735fd7f25947a3909998420affa"

    file, err := os.Open("/Users/fudenglong/Downloads/WechatIMG455.jpeg")
    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/WechatIMG455.jpeg",
            "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 := libHttp.DefaultClient.Do(request)
        if err != nil {
            fmt.Println(err)
            return
        }

        if bodyBytes, err := ioutil.ReadAll(resp.Body); err != nil {
            fmt.Println(err)
            return
        } else {
            fmt.Println(string(bodyBytes))
        }

    }
}

在这个例子中,我们上传了一个 4.7 MB 的文件,但是该接口每次仅限上传 1 MB 的文件,所以我们将文本分割成 1 MB 再去上传它。

而且,我们在上传第一个分片的时候声明如果该路径上的文件已经存在,那么就去覆盖它,随后的分片追加到该路径表的文件中。

需要注意的是,如果您选择如果在路径存在时重命名,那么您应该在第一个分片上传之后判断它,如果返回的文件和你指定的上传路径不一致,在追加随后的分片的时候应该使用第一个分片上传之后返回的路径。

这个例子在最后一次请求响应成功后会返回下面的结果:

{
    "requestId":10020,
    "success":true,
    "errors":null,
    "data":{
        "ext":"jpeg",
        "fileUid":"64c8a8ecd911630acf1dc26e8319f2dd",
        "hash":"9536467bde347627e27634a77963105a045f624e290b0f2bbc342834abdd4593",
        "hidden":0,
        "isDir":0,
        "path":"/images/png/profile/WechatIMG455.jpeg",
        "size":4698744
    }
}

fileUid 是一个重要的字段,是每个文件或者目录的唯一凭证,isDir=0 表示它不是一个目录,hash 表示整个文件的 hash 值,您可以用此值去验证文件的完整性。size 表示文件的大小,单位:byte1 MB = 1024 * 1024 byte

创建目录示例

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "mime/multipart"
    libHttp "net/http"

    "github.com/bigfile/bigfile/databases/models"
    "github.com/bigfile/bigfile/http"
)

func main() {
    var (
        err            error
        body           = new(bytes.Buffer)
        request        *libHttp.Request
        token          = "49f92acd696260abba1bc4062d157199"
        tokenSecret    = "9bcac735fd7f25947a3909998420affa"
        formBodyWriter = multipart.NewWriter(body)
    )
    params := map[string]interface{}{
        "token": token,
        "path":  "/profiles",
        "nonce": models.RandomWithMd5(255),
    }
    params["sign"] = http.GetParamsSignature(params, tokenSecret)
    for k, v := range params {
        if err = formBodyWriter.WriteField(k, v.(string)); 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 := libHttp.DefaultClient.Do(request)
    if err != nil {
        fmt.Println(err)
        return
    }

    if bodyBytes, err := ioutil.ReadAll(resp.Body); err != nil {
        fmt.Println(err)
        return
    } else {
        fmt.Println(string(bodyBytes))
    }
}

这个请求之后将会得到如下的响应:

{
    "requestId":10025,
    "success":true,
    "errors":null,
    "data":{
        "fileUid":"03ccc6c26ec7c3a0fe2be90c3f3882d0",
        "hidden":0,
        "isDir":1,
        "path":"/images/png/profiles",
        "size":0
    }
}

isDir=1 表示它是一个目录,size 表示该目录下所有子文件以及子目录大小之和。

英文文档:bigfile.site

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
Michael001
讨论数量: 0
发起讨论 只看当前版本


暂无话题~