分片上传

SDK 提供了分片上传(Multipart Upload)功能,可以将要上传的文件分成多个数据块(US3 里又称之为 Part)来分别上传。完整代码详见 Github

分片上传方式

SDK 目前提供了如下几种分片上传方式:

参数名 说明
MPut 同步分片上传,非并发
AsyncMPut 异步分片并发上传,并发数10
AsyncUpload 异步分片并发上传,并发数可调
IOMutipartAsyncUpload 流式并发分片上传

同步分片上传

MPut实现同步分片上传,示例如下,完整代码详见 Github

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
	ufsdk "github.com/ufilesdk-dev/ufile-gosdk"
	"log"
)

const (
	ConfigFile = "config.json"
	FilePath = "FakeBigFile.txt" 
	KeyName = "FakeBigFile.txt" 
	MimeType = ""
)

func main() {

	// 加载配置,创建请求
	config, err := ufsdk.LoadConfig(ConfigFile)
	if err != nil {
		panic(err.Error())
	}
	req, err := ufsdk.NewFileRequest(config, nil)
	if err != nil {
		panic(err.Error())
	}

	// 同步分片上传本地文件
	err = req.MPut(FilePath,  KeyName, MimeType)
	if err != nil {
		log.Fatalf("%s\n", err.Error())
	}
	log.Println("文件上传成功!!")
}

异步分片上传

AsycMPut实现异步分片上传,固定10个分片并发上传,示例如下,完整代码详见 Github

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
	ufsdk "github.com/ufilesdk-dev/ufile-gosdk"
	"log"
)

const (
	ConfigFile = "config.json"
	FilePath = "FakeBigFile.txt"
	KeyName = "FakeBigFile.txt"
	MimeType = ""
)

func main() {

	// 加载配置,创建请求
	config, err := ufsdk.LoadConfig(ConfigFile)
	if err != nil {
		panic(err.Error())
	}
	req, err := ufsdk.NewFileRequest(config, nil)
	if err != nil {
		panic(err.Error())
	}

	// 异步分片上传本地文件
	err = req.AsyncMPut(FilePath,  KeyName, MimeType)
	if err != nil {
		log.Fatalf("%s\n", err.Error())
	}
	log.Println("文件上传成功!!")
}

异步并发分片上传

AsyncUpload实现了异步并发分片上传,可自定义并发上传数量,目前允许并发数需小于30,适用于大文件加速上传。示例如下,完整代码详见 Github

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
	ufsdk "github.com/ufilesdk-dev/ufile-gosdk"
	"log"
)

const (
	ConfigFile = "config.json"
	FilePath = "FakeBigFile.txt"
	KeyName = "FakeBigFile.txt"
	MimeType = ""
	Jobs = 29
)

func main() {

	// 加载配置,创建请求
	config, err := ufsdk.LoadConfig(ConfigFile)
	if err != nil {
		panic(err.Error())
	}
	req, err := ufsdk.NewFileRequest(config, nil)
	if err != nil {
		panic(err.Error())
	}

	// 异步分片上传本地文件
	err = req.AsyncUpload(FilePath,  KeyName, MimeType, Jobs)
	if err != nil {
		log.Fatalf("%s\n", err.Error())
	}
	log.Println("文件上传成功!!")
}

流式并发分片上传

IOMultipartAsyncUpload通过传入io.Reader 实现流式并发分片上传,使用于文件大小还不确定的情况下开始上传。示例如下,完整代码详见 Github

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import (
	ufsdk "github.com/ufilesdk-dev/ufile-gosdk"
	"log"
	"os"
)

const (
	ConfigFile = "config.json"
	FilePath = "FakeBigFile.txt"
	KeyName = "FakeBigFile.txt"
	MimeType = ""
)

func main() {

	// 加载配置,创建请求
	config, err := ufsdk.LoadConfig(ConfigFile)
	if err != nil {
		panic(err.Error())
	}
	req, err := ufsdk.NewFileRequest(config, nil)
	if err != nil {
		panic(err.Error())
	}

	// 流式分片上传本地文件
	f, err := os.Open(FilePath)
	if err != nil {
		panic(err.Error())
	}
	err = req.IOMutipartAsyncUpload(f, KeyName, MimeType)
	if err != nil {
		log.Fatalf("%s\n", err.Error())
	}
	log.Println("文件上传成功!!")
}

实际上,一个完整的分片上传分为初始化分片 -> 上传分片 -> 完成分片三个阶段,具体见分片上传流程

分片上传流程

SDK 提供以下方法完成分片上传,接口说明如下

接口名 说明
InitiateMultipartUpload 初始化分片上传,US3 API 文档详见 InitiateMultipartUpload
UploadPart 上传一个分片,US3 API 文档详见 UploadPart
AbortMultipartUpload 终止分片上传,US3 API 文档详见 AbortMultipartUpload
FinishMultipartUpload 完成分片上传,US3 API 文档详见 FinishMultipartUpload

分片上传流程示例

一个完整的本地文件分片上传流程示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package main

import (
	"bytes"
	ufsdk "github.com/ufilesdk-dev/ufile-gosdk"
	"io"
	"os"
)

const (
	ConfigFile = "config.json"
	FilePath = "FakeBigFile.txt" 
	KeyName = "FakeBigFile.txt" 
	MimeType = ""
)

func checkErr (err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	// 加载配置,创建请求
	config, err := ufsdk.LoadConfig(ConfigFile)
	if err != nil {
		panic(err.Error())
	}
	req, err := ufsdk.NewFileRequest(config, nil)
	if err != nil {
		panic(err.Error())
	}

	file, err := os.Open(FilePath)
	checkErr(err)
	defer file.Close()

	// 初始化分片
	state, err := req.InitiateMultipartUpload(KeyName, MimeType)
	checkErr(err)
	// 逐个上传分片, 若出错则终止上传
	chunk := make([]byte, state.BlkSize)
	var pos int
	for {
		bytesRead, fileErr := file.Read(chunk)
		if fileErr == io.EOF || bytesRead == 0 { //后面直接读到了结尾
			break
		}
		buf := bytes.NewBuffer(chunk[:bytesRead])
		err := req.UploadPart(buf, state, pos)
		if err != nil {
			req.AbortMultipartUpload(state)
			checkErr(err)
		}
		pos++
	}
	// 完成分片
	err = req.FinishMultipartUpload(state)
	checkErr(err)
}

错误码

HTTP 状态码 RetCode ErrMsg 描述
400 -148653 bucket not exists 存储空间不存在
400 -15036 check md5 failed MD5校验失败
401 -148643 no authorization found 上传凭证错误
403 -148643 invalid signature API公私钥错误KeyName包含%、#、?