总结阿里云OSS的开发坑(C/C++篇)

一、序言

OSS(Object Storage Service)是阿里云提供的一款云存储服务,具有海量、安全、低成本、高可靠的特点。

由于客户选择了OSS,我们作为开发方也开始接触它。在实际开发过程中遇到了各种各样的坑,经自己多次实践及阿里技术人员的协助,终得以完成任务。

阿里方面为OSS提供了多种语言的开发接口,我们用到了其中两种:Java和C/C++。本文为C/C++篇,Java的已在另一篇给出。

二、OSS的一些概念

  • EndPoint, accessKeyID, accessKeySecret:欲使用OSS,先要在阿里云上申请相应的空间资源,而EndPoint, accessKeyID, accessKeySecret则相当于域名、账号和密码,是所申请资源的使用凭证,需要妥善保管。
  • Bucket:是用于存储对象的容器,所有对象都必须属于且只属于一个Bucket,Bucket的属性(控制地域、访问权限、生命周期等)对所有对象都同等有效,同一空间资源下Bucket名必须唯一,且创建后不能再改名。
  • 对象/文件:对象/文件是 OSS 存储数据的基本单元。对象/文件由元信息(Object Meta),用户数据(Data)和文件名(Key)组成。文件名是唯一的,重复上传同名的对象意味着覆盖以前内容,但OSS支持在已有对象后部追加数据。
  • 目录:其实是一种特殊的对象(无Data),仅仅是为了管理方便,除此以外并无多大意义。
  • 其它概念,如分片、回调、追加、权限等,因开发中未涉及,不再展开,有兴趣者请参考:https://help.aliyun.com/document_detail/31827.html?spm=a2c4g.11186623.4.1.TamX1d

 三、1号坑:C SDK提供的接口较少

严格地讲,也许这算不上是坑,权当是本文为凑数。

与Java SDK相比,C SDK中缺少一些接口,比如判断某个Bucket是否已经存在,Java SDK中有,而C SDK中没有。

四、2号坑:Windows平台的64位库

一开始阿里云只提供了Windows平台的32位库(动态和静态),主要原因是oss-c-sdk所使用的依赖库在windows平台需经很多修改才支持64位。后来在客户的强烈要求下,阿里云方面紧急补充了64位Windows库。在此非常感谢阿里云工程师的辛勤工作。

不过,64位库貌似不是很稳定,运行时程序偶尔会奔溃。当然,这与本人C/C++水平比较菜的不无关系,这口锅是由本人来背好了。

五、3号坑:关于EndPoint的特殊格式

通常情况下,OSS所用到的endPoint类似这样:oss-cn-beijing.aliyuncs.com。

但在某些场合(如本项目客户方的私有云),endPoint中包含了Bucket名,类似这样:oss-cn-beijing.aliyuncs.com/bucket01。

在Java程序中,只须将Bucket名从中解析出来,不必修改endPoint,可以正常运行。但在C/C++程序中,这样做会报错“Invalid host name”。

正确的做法是:将包含有Bucket名的EndPoint拆分为两部分,"/"前为endPoint,后为Bucket名,问题解决。

六、4号坑:关于保存endPoint、accessKeyId、accessKeySecret值的变量

这是本项目遇到过的最大坑,没有之一。

在阿里提供的示例代码中,使用的是常量直接赋值,不会出任何问题。但本项目把这些值放在配置文件中,由程序读出到局部变量后再赋值,问题就来了,运行一会这些值就变成乱码。

后来把oss-c-sdk的源码、所依赖底层库的源码都加进去调试跟踪,才找到原因所在:aos_str_set函数只是简单地赋地址指针的值,而没有执行memcpy,因此一旦有函数传递,局部变量的地址空间被回收,值也变成乱码。

解决方法:使用全局变量或类成员变量来保存从配置文件读取到的值,确保在程序的生命周期内不会被回收。

头文件示例代码:

        class OssMntData : public MntData
        {
        private:
// 从配置文件读取的参数必须保持地址空间,不能使用临时变量
            char                    m_endpoint[64];
            char                    m_keyid[64];
            char                    m_keysecret[64];
            char                    m_bucketname[64];
            ...
       public:
            ...
       }

C/C++文件示例代吗:

        aos_pool_t *pool = NULL;
        oss_request_options_t *options = NULL;

        aos_pool_create(&pool, NULL);
        options = oss_request_options_create(pool);
        options->config = oss_config_create(options->pool);
        aos_str_set(&options->config->endpoint, m_endpoint);
        aos_str_set(&options->config->access_key_id, m_keyid);
        aos_str_set(&options->config->access_key_secret, m_keysecret);
        options->config->is_cname = 0;
        options->ctl = aos_http_controller_create(options->pool, 0);

七、其它坑

与Java程序一样,如果遍历OSS对象的同时进行修改,会陷入死循环。请参加Java篇,不再重复。

如果使用UserMetaData保存属性值,其key会在存入时自动转换为小写。

提醒:C SDK中,UserMetaData中的key须加上“x-oss-meta-”的前缀,以示与自带的元数据区别。而在Java代码中没有这个要求。

示例代码:

    int OssMntData::Write()
    {    
      //将相关属性值存放到metadata中
      aos_table_t *header = aos_table_make(pool, 1);
      apr_table_set(header, "x-oss-meta-author", author);
      apr_table_set(header, "x-oss-meta-version", version);
      ...
      aos_status_t *status = oss_put_object_from_buffer(options, &m_bucket, &ossKey, &buffer, header, &resp_header);
         ...
    }            

    int OssMntData::Read()
    {
      //读取UserMeta,为各属性字段赋值
      header = aos_table_make(pool, 0);
      resp_header = NULL;
      status = oss_head_object(options, &m_bucket, &ossKey, header, &resp_header);
      char *s = (char *)apr_table_get(resp_header, "x-oss-meta-author");
      if (s) strcpy(mnt->author, s);
      s = (char *)apr_table_get(resp_header, "x-oss-meta-version");
      if (s) strcpy(mnt->version, s);
      ...
    }

 

posted @ 2018-06-13 11:02  闻歌感旧  阅读(4520)  评论(1编辑  收藏  举报