总结阿里云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); ... }