对象存储

  • C/C++ SDK

    最近更新时间:2018-10-17 10:48:24

    本 SDK 使用符合 C89 标准的 C 语言实现。由于 C 语言的普适性,原则上此 SDK 可以跨所有主流平台,不仅可以直接在 C 和 C++ 的工程中使用,也可以用于与 C 语言交互性较好的语言中,例如 C#(使用 P/Invoke 交互)、Java(使用 JNI 交互)、Lua 等。

    本开发指南假设开发者使用的开发语言是 C/C++,C-SDK 以开源方式提供。开发者可以从本文档提供的下载地址查看和下载 SDK 的源代码,并按自己的工程现状进行合理使用,例如编译为静态库或者动态库后进行链接,或者直接将 SDK 的源代码加入到自己的工程中一起编译,以保持工程设置的简单性。

    从 v5.0.0 版本开始,我们对 SDK 的内容进行了精简。所有管理操作,
    例如创建/删除 bucket,为 bucket 绑定域名 (publish),设置数据处理的样式分隔符,新增数据处理样式等都去除了,建议统一到七牛开发者平台完成。另外,此前服务端还有自己独有的上传 API,现在也推荐统一成基于客户端上传的工作方式。

    从内容上来说,C-SDK 主要包含如下几方面的内容:

    • 公共部分,所有情况下都用到:qiniu/base.c、qiniu/conf.c、qiniu/http.c
    • 客户端上传文件:qiniu/base_io.c、qiniu/io.c
    • 客户端断点续上传:qiniu/base_io.c、qiniu/io.c、qiniu/resumable_io.c
    • 数据处理:qiniu/fop.c
    • 服务端操作:qiniu/auth_mac.c(授权),qiniu/rs.c(资源管理,上传凭证 uptoken/下载凭证 dntoken 生成)
    • cdn相关功能:qiniu/cdn.c

    安装

    C-SDK 使用cURL进行网络相关操作。无论是作为客户端还是服务端,都需要依赖 cURL。如果作为服务端,C-SDK 因为需要用 HMAC 进行数字签名做授权(简称签名授权),所以依赖了OpenSSL库。C-SDK 并没有带上这 2 个外部库,因此在使用 C-SDK 之前,首先确认您的当前开发环境中是否已经安装了这 2 个外部库,然后检查这 2 个外部库的头文件目录和库文件目录是否都已经加入到了项目工程的设置。

    在主流的 *nix 环境下,通常cURLOpenSSL都已经随系统默认安装到了 /usr/include 和 /usr/lib 目录下。如果您的系统还没有这些库,请自行安装。

    如果你使用 gcc 进行编译,服务端典型的链接选项是:-lcurl -lssl -lcrypto -lm,客户端则是:-lcurl -lm。

    如果在项目构建过程中出现环境相关的编译错误和链接错误,请确认这些选项是否都已经正确配置,以及所依赖的库是否都已经正确安装。

    ACCESS_KEY 和 SECRET_KEY

    如果你的服务端采用 C-SDK,那么使用 C-SDK 前,您需要拥有一对有效的 AccessKey 和 SecretKey 用来进行签名授权。可以通过如下步骤获得:

    1. 开通七牛开发者帐号
    2. 登录七牛开发者自助平台,在个人面板 查看 AccessKey 和 SecretKey

    C-SDK 的 conf.h 文件中声明了对应的两个变量:QINIU_ACCESS_KEY 和 QINIU_SECRET_KEY。您需要在启动程序之初初始化这两个变量为七牛颁发的 AccessKey 和 SecretKey。

    初始化

    对于服务端而言,常规程序流程是:

    Qiniu_Client client;
    
    QINIU_ACCESS_KEY = "<Please apply your access key>";
    QINIU_SECRET_KEY = "<Dont send your secret key to anyone>";
    
    Qiniu_Servend_Init(-1);                        /* 全局初始化函数,整个进程只需要调用一次 */
    Qiniu_Client_InitMacAuth(&client, 1024, NULL); /* HTTP客户端初始化。HTTP客户端是线程不安全的,不要在多个线程间共用 */
    
    ...
    
    Qiniu_Client_Cleanup(&client);                 /* 每个HTTP客户端使用完后释放 */
    Qiniu_Servend_Cleanup();                       /* 全局清理函数,只需要在进程退出时调用一次 */
    

    对于客户端而言,常规程序流程是:

    Qiniu_Client client;
    
    Qiniu_Global_Init(-1);                  /* 全局初始化函数,整个进程只需要调用一次 */
    Qiniu_Client_InitNoAuth(&client, 1024); /* HTTP客户端初始化。HTTP客户端是线程不安全的,不要在多个线程间共用 */
    
    ...
    
    Qiniu_Client_Cleanup(&client);          /* 每个HTTP客户端使用完后释放 */
    Qiniu_Global_Cleanup();                 /* 全局清理函数,只需要在进程退出时调用一次 */
    

    两者主要的区别在于:

    • 客户端没有 QINIU_ACCESS_KEY、QINIU_SECRET_KEY 变量(不需要初始化)。

    • 客户端没有签名授权,所以初始化 Qiniu_Client 对象应该用 Qiniu_Client_InitNoAuth 而不是 Qiniu_Client_InitMacAuth。

    • 客户端初始化/清理用 Qiniu_Global_Init/Cleanup,而服务端用 Qiniu_Servend_Init/Cleanup 这对函数。

    内存管理

    在 C-SDK 中,有一些函数会涉及到内存的动态分配。这些函数的一惯处理方式是在函数内部申请内存,并以指针的形式直接返回。这就要求函数调用者在得到指针后,需要在恰当的时机去释放这些内存。对于特殊的结构体,C-SDK 都会提供特定的函数来释放内存,例如 Qiniu_Buffer 提供了 Qiniu_Buffer_Cleanup 函数。而对于其他基本数据类型的指针,则由 Qiniu_Free 函数来负责释放不再使用的内存。

    HTTP 客户端

    在 C-SDK 中,HTTP 客户端叫 Qiniu_Client。在某些语言环境中,这个类是线程安全的,多个线程可以共享同一份实例,但在 C-SDK 中它被设计为线程不安全的。一个重要的原因是我们试图简化内存管理的负担。HTTP 请求结果的生命周期被设计成由 Qiniu_Client 负责,在下一次请求时会自动释放上一次 HTTP 请求的结果。如果某个 HTTP 请求结果的数据需要长期使用,建议您复制一份,如:

    void stat(Qiniu_Client* client, const char* bucket, const char* key) {
        Qiniu_RS_StatRet ret;
        Qiniu_Error err = Qiniu_RS_Stat(client, &ret, bucket, key);
        if (err.code != 200) {
            debug(client, err);
            return;
        }
        printf("hash: %s, fsize: %lld, mimeType: %s\n", ret.hash, ret.fsize, ret.mimeType);
    }
    

    这个例子中,Qiniu_RS_Stat 请求返回了 Qiniu_Error 和 Qiniu_RS_StatRet 两个结构体。其中的 Qiniu_Error 类型如下:

    typedef struct _Qiniu_Error {
        int code;
        const char* message;
    } Qiniu_Error;
    

    Qiniu_RS_StatRet 类型如下:

    typedef struct _Qiniu_RS_StatRet {
        const char* hash;
        const char* mimeType;
        Qiniu_Int64 fsize;    
        Qiniu_Int64 putTime;
        Qiniu_Int64 type;
    } Qiniu_RS_StatRet;
    

    注意:Qiniu_Error.message、Qiniu_RS_StatRet.hash、Qiniu_RS_StatRet.mimeType 都声明为 const char* 类型,是个只读字符串,并不管理字符串内容的生命周期。
    这些字符串什么时候失效?下次 Qiniu_Client 发生网络 API 请求时失效。如果您需要长久使用,建议您复制一份,如:

    hash = strdup(ret.hash);
    

    错误处理与调试

    在 HTTP 请求出错的时候,C-SDK 统一返回了一个 Qiniu_Error 结构体:

    typedef struct _Qiniu_Error {
        int code;
        const char* message;
    } Qiniu_Error;
    

    即一个错误码和对应的描述消息。这个错误码有可能是 cURL 的错误码,表示请求发送环节发生了意外,或者是一个 HTTP 错误码,表示请求发送正常,服务器端处理请求后返回了 HTTP 错误码。

    如果一切正常,code 应该是 200,即 HTTP 的 OK 状态码。如果不是 200,则需要对 code 值进行相应分析。对于低于 200 的值,可以查看cURL错误码,否则应查看七牛HTTP状态码

    如果 message 指示的信息还不够友好,也可以尝试把整个 HTTP 返回包打印出来看看:

    void debug(Qiniu_Client* client, Qiniu_Error err)
    {
        printf("\nerror code: %d, message: %s\n", err.code, err.message);
        printf("respose header:\n%s", Qiniu_Buffer_CStr(&client->respHeader));
        printf("respose body:\n%s", Qiniu_Buffer_CStr(&client->b));
    }
    

    文件上传

    上传流程

    在七牛云存储中,整个上传流程大致分为以下几步:

    1. 业务服务器颁发上传凭证给客户端(终端用户)
    2. 客户端凭借上传凭证上传文件到七牛
    3. 在七牛获得完整数据后,发起一个 HTTP 请求回调到业务服务器
    4. 业务服务器保存相关信息,并返回一些信息给七牛
    5. 七牛原封不动地将这些信息转发给客户端(终端用户)

    注意:回调到业务服务器的过程是可选的,它取决于业务服务器颁发的上传凭证。如果没有回调,七牛会返回一些标准的信息(例如文件的 hash)给客户端。
    如果上传发生在业务服务器,以上流程简化为:

    1. 业务服务器生成上传凭证