一、序言
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); ... }