本 SDK 提供了分片上传(Multipart Upload)功能,可以将要上传的文件分成多个数据块(US3 里又称之为 Part)来分别上传,适用于大文件。
分片上传流程
在US3中,一个完整的分片上传分为初始化分片 -> 上传分片 -> 完成分片(放弃分片)三个阶段。本SDK提供的相应方法如下,完整代码详见 Github
方法名 | 说明 |
---|---|
ufile_multiple_upload_init | 初始化分片上传,US3 API 文档详见 InitiateMultipartUpload |
ufile_multiple_upload_part | 上传一个分片,US3 API 文档详见 UploadPart |
ufile_multiple_upload_finish | 终止分片上传,US3 API 文档详见 AbortMultipartUpload |
ufile_multiple_upload_abort | 完成分片上传,US3 API 文档详见 FinishMultipartUpload |
目前SDK提供同步分片上传和异步并发分片上传两种方式,示例如下。
同步分片上传
注意,当前SDK并未直接提供同步分片上传方法供外部调用,若使用同步分片上传请参照示例实现。如有疑问,可到Github上提交issue或联系我们;
示例
执行该示例请先配置
UFILE_*
相关环境变量并赋予bucket_name
、key
、mime_type
和file_path
有效值。
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
61
62
63
64
65
#include "../lib/api.h"
#include <stdio.h>
#include <stdlib.h>
const char* bucket_name = "your bucket name";
const char* key = "your file key";
const char* file_path = "your local file to be uploaded";
const char* mime_type = "your file type";
int main(int argc, char *argv[]){
// 读取配置初始化SDK
struct ufile_config cfg;
cfg.public_key = getenv("UFILE_PUBLIC_KEY");
cfg.private_key = getenv("UFILE_PRIVATE_KEY");
cfg.bucket_host = getenv("UFILE_BUCKET_HOST");
cfg.file_host = getenv("UFILE_FILE_HOST");
struct ufile_error error;
error = ufile_sdk_initialize(cfg, 0);
if(UFILE_HAS_ERROR(error.code)){
printf("初始化 sdk 失败,错误信息为:%s\n", error.message);
return 1;
}
// 初始化分片
struct ufile_mutipart_state state;
error = ufile_multiple_upload_init(&state, bucket_name, key, mime_type);
if(UFILE_HAS_ERROR(error.code)){
ufile_sdk_cleanup();
printf("调用 ufile_multiple_upload_init 失败,错误信息为:%d, %s\n", error.code, error.message);
return 1;
}
FILE *fp = fopen(file_path, "rb");
if (fp == NULL){
fprintf(stderr, "打开文件失败, 错误信息为: %s\n", strerror(errno));
return 1;
}
// 上传分片
char *buf = malloc(state.part_size);
for(int i=0; ; i++){
size_t nc = fread(buf, 1, state.part_size, fp);
if(nc == 0){ // 为0即读到文件尾,分片已上传完毕
break;
}
error = ufile_multiple_upload_part(&state, buf, nc, i);
if(UFILE_HAS_ERROR(error.code)){
printf("调用 ufile_multiple_upload_part 失败,错误信息为:%d, %s\n", error.code, error.message);
// 分片上传出错,放弃分片
ufile_multiple_upload_abort(&state);
ufile_sdk_cleanup();
return 1;
}
}
// 完成分片上传
printf("调用 ufile_multiple_upload_finish 完成分片上传\n");
error = ufile_multiple_upload_finish(&state);
if(UFILE_HAS_ERROR(error.code)){
printf("调用 ufile_multiple_upload_finish 失败,错误信息为:%s\n", error.message);
}
// 释放初始化SDK时分配的内存
ufile_sdk_cleanup();
return 0;
}
异步并发分片上传
注意,当前SDK的异步并发上传功能仅做测试使用,不包含在sdk模块中。线程池模块完整代码请详见Github。若要使用异步并发分片上传请参照以下示例。如有疑问,可到Github上提交issue或联系我们;
示例
执行该示例请先配置
UFILE_*
相关环境变量并赋予bucket_name
、key
、mime_type
和file_path
有效值。
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include "../lib/api.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "thpool.h" //线程池模块,仅做为测试用,不包含在sdk模块中。
const char* bucket_name = "your bucket name";
const char* key = "your file key";
const char* file_path = "your local file to be uploaded";
const char* mime_type = "your file type";
const int jobs = 5;
struct part_data{
struct ufile_mutipart_state *state;
char *buf;
size_t buf_len;
int pos;
};
// 线程的分片上传任务,param为分片数据
void upload_task(void *param){
struct part_data *part = (struct part_data*)param;
struct ufile_error error;
error = ufile_multiple_upload_part(part->state, part->buf, part->buf_len, part->pos);
if(UFILE_HAS_ERROR(error.code)){
printf("调用 upload 失败,错误信息为:%s\n", error.message);
ufile_multiple_upload_abort(part->state); // 某一分片上传失败,则放弃分片上传
exit(1); //某一分片上传失败,放弃分片上传后直接退出程序。
}
free(part->buf);
free(part);
}
int main(int argc, char *argv[]){
// 读取配置初始化SDK
struct ufile_config cfg;
cfg.public_key = getenv("UFILE_PUBLIC_KEY");
cfg.private_key = getenv("UFILE_PRIVATE_KEY");
cfg.bucket_host = getenv("UFILE_BUCKET_HOST");
cfg.file_host = getenv("UFILE_FILE_HOST");
struct ufile_error error;
error = ufile_sdk_initialize(cfg, 0);
if(UFILE_HAS_ERROR(error.code)){
printf("初始化 sdk 失败,错误信息为:%s\n", error.message);
return 1;
}
// 初始化分片
struct ufile_mutipart_state state;
error = ufile_multiple_upload_init(&state, bucket_name, key, file_path);
if(UFILE_HAS_ERROR(error.code)){
ufile_sdk_cleanup();
printf("调用 ufile_multiple_upload_init 失败,错误信息为:%d, %s\n", error.code, error.message);
return 1;
}
// 初始化线程池,线程数量为 jobs
threadpool thpool = thpool_init(jobs);
FILE *fp = fopen(file_path, "rb");
int i;
for(i=0; ; i++){
struct part_data *part = malloc(sizeof(struct part_data));
part->state = &state;
part->buf = malloc(state.part_size);
part->buf_len = fread(part->buf, 1, state.part_size, fp);
part->pos = i;
if(part->buf_len == 0){
free(part->buf);
free(part);
break;
}
// 为每个分片添加一个分片任务到线程池的工作队列中
thpool_add_work(thpool, &upload_task, part);
}
// 等待线程池中的所有分片上传任务完成
thpool_wait(thpool);
// 分片上传任务完成,销毁线程池
thpool_destroy(thpool);
// 完成分片上传
error = ufile_multiple_upload_finish(&state);
if(UFILE_HAS_ERROR(error.code)){
printf("调用 ufile_multiple_upload_part 失败,错误信息为:%s\n", error.message);
}
// 释放初始化SDK时分配的内存
ufile_sdk_cleanup();
return 0;
}