分片上传

本 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_namekeymime_typefile_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_namekeymime_typefile_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;
}