使用osscsdk自定義上傳和下載callback

yjseu發表於2016-02-01

       前段時間使用阿里雲官網提供的OSS C SDK上傳和下載資料,想在上傳和下載過程中對資料進行一些簡單的自定義預處理,看了一下oss c sdk的具體實現,大致瞭解如何通過自定義上傳和下載的callback達到上述目的,這裡做一個簡單的分享。

       OSS C SDK在上傳和下載資料時使用了CURL進行通訊,之前簡單學習過CURL的一些知識,知道CURL提供了一系列Callback,在上傳下載時對資料進行一些處理,大家感興趣的話可以參考: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html 。OSS C SDK針對OSS的常用場景,對使用者暴露了兩個最基本的Callback:CURLOPT_WRITEFUNCTION 和 CURLOPT_READFUNCTION。結合OSS C SDK使用這兩個Callback實現上傳資料和下載資料,可以大致瞭解整個過程。

1、上傳資料

      OSS C SDK上傳資料提供了兩種方式:從memory讀取、從檔案讀取。以從檔案讀取資料為例,我們看一下如何利用Callback實現從檔案讀取資料上傳到OSS的。 
首先,我們熟悉oss c sdk中使用的一些資料結構: 
struct aos_http_request_s { 
    char *host; 
    uint8_t port; 
    char *signed_url; 
 
    http_method_e method; 
    char *uri; 
    char *resource; 
    aos_table_t *headers; 
    aos_table_t *query_params; 
 
    aos_list_t body; 
    int64_t body_len; 
    char *file_path; 
    aos_file_buf_t *file_buf; 
 
    aos_pool_t *pool; 
    void *user_data; 
    aos_read_http_body_pt read_body; 
 
    aos_http_body_type_e type; 
}; 
 
typedef enum { 
    BODY_IN_MEMORY = 0, 
    BODY_IN_FILE, 
    BODY_IN_CALLBACK 
aos_http_body_type_e; 
 
typedef int (*aos_read_http_body_pt)(aos_http_request_t *req, char *buffer, int len); 

OSS C SDK提供了oss_put_object_from_file介面用於從檔案讀取資料上傳到OSS,重點看一下oss_write_request_body_from_file的實現: 
 
int oss_write_request_body_from_file(aos_pool_t *p, const aos_string_t *filename, aos_http_request_t *req) 
{ 
    int res = AOSE_OK; 
    aos_file_buf_t *fb = aos_create_file_buf(p); 
    res = aos_open_file_for_all_read(p, filename->data, fb); 
    if (res != AOSE_OK) { 
        aos_error_log(“Open read file fail, filename:%s
“, filename->data);
 
        return res; 
    } 
 
    req->body_len = fb->file_last; 
    req->file_path = filename->data; 
    req->file_buf = fb; 
    req->type = BODY_IN_FILE; 
    req->read_body = aos_read_http_body_file; 
 
    return res; 
} 
 

和上面的aos_http_request_s資料結構聯絡起來,可以看到在這裡設定讀取檔案的相關資訊的。接下來看一下這些設定如何和CURL提供的Callback聯絡上。

aos_http_transport_t *aos_curl_http_transport_create(aos_pool_t *p) 
{ 
    aos_func_u func; 
    aos_curl_http_transport_t *t; 
 
    t = (aos_curl_http_transport_t *)aos_pcalloc(p, sizeof(aos_curl_http_transport_t)); 
 
    t->pool = p; 
    t->options = aos_default_http_transport_options; 
    t->cleanup = aos_fstack_create(p, 5); 
 
    func.func1 = (aos_func1_pt)aos_transport_cleanup; 
    aos_fstack_push(t->cleanup, t, func, 1); 
 
    t->curl = aos_request_get(); 
    func.func1 = (aos_func1_pt)request_release; 
    aos_fstack_push(t->cleanup, t->curl, func, 1); 
 
    t->header_callback = aos_curl_default_header_callback; 
   t->read_callback = aos_curl_default_read_callback; 
    t->write_callback = aos_curl_default_write_callback; 
 
    return (aos_http_transport_t *)t; 
} 
 
size_t aos_curl_default_read_callback(char *buffer, size_t size, size_t nitems, void *instream) 
{ 
    int len; 
    int bytes; 
    aos_curl_http_transport_t *t; 
 
    t = (aos_curl_http_transport_t *)(instream); 
    len = size * nitems; 
 
    if (t->controller->error_code != AOSE_OK) { 
        aos_debug_log(“abort read callback.”); 
        return CURL_READFUNC_ABORT; 
    } 
 
    if ((bytes = t->req->read_body(t->req, buffer, len)) < 0) { 
        aos_debug_log(“read body failure, %d.”, bytes); 
        t->controller->error_code = AOSE_READ_BODY_ERROR; 
        t->controller->reason = “read body failure.”; 
        return CURL_READFUNC_ABORT; 
    } 
 
    aos_move_transport_state(t, TRANS_STATE_BODY_OUT); 
 
    return bytes; 
} 
 
int aos_curl_transport_setup(aos_curl_http_transport_t *t) 
{ 
     … 
     curl_easy_setopt_safe(CURLOPT_READFUNCTION, t->read_callback); 
     … 
} 
 
紅色部分基本已經說明了OSS C SDK如何呼叫CURL提供的CURLOPT_READFUNCTION了。參考OSS C SDK上傳資料的實現大概也就知道如何對上傳資料做一些簡單的自定義了。

2、下載資料

下載資料常見的方式有兩種:下載資料到memory、下載資料到檔案。以下載資料到檔案中為例,OSS C SDK使用CURL提供的Callback方式實現了資料下載。首先,熟悉一下OSS C SDK中使用的一些資料結構: 
 
struct aos_http_response_s { 
    int status; 
    aos_table_t *headers; 
 
    aos_list_t body; 
    int64_t body_len; 
    char *file_path; 
    aos_file_buf_t* file_buf; 
    int64_t content_length; 
 
    aos_pool_t *pool; 
    void *user_data; 
    aos_write_http_body_pt write_body; 
    aos_http_body_type_e type; 
}; 
 
typedef enum {      
    BODY_IN_MEMORY = 0,      
    BODY_IN_FILE,      
    BODY_IN_CALLBACK  
aos_http_body_type_e; 
 
typedef int (*aos_write_http_body_pt)(aos_http_response_t *resp, const char *buffer, int len); 
 
       OSS C SDK提供了oss_get_object_to_file介面用於將OSS上的資料下載到本地檔案中。重點看一下這個函式中oss_init_read_response_body_to_file函式的實現: 
 
int oss_init_read_response_body_to_file(aos_pool_t *p, const aos_string_t *filename, aos_http_response_t *resp) 
{ 
    int res = AOSE_OK; 
    aos_file_buf_t *fb = aos_create_file_buf(p); 
    res = aos_open_file_for_write(p, filename->data, fb); 
    if (res != AOSE_OK) { 
        aos_error_log(“Open write file fail, filename:%s
“, filename->data);
 
        return res; 
    } 
    resp->file_path = filename->data; 
    resp->file_buf = fb; 
    resp->write_body = aos_write_http_body_file; 
    resp->type = BODY_IN_FILE; 

    return res; 
} 
 
    這部分和上面的aos_http_response_s資料結構聯絡起來,可以設定將下載資料寫入本地檔案。接下來看一下這些設定如何和CURL提供的Callback聯絡上。
aos_http_transport_t *aos_curl_http_transport_create(aos_pool_t *p) {      
    aos_func_u func;      
    aos_curl_http_transport_t *t;  
    
    t = (aos_curl_http_transport_t *)aos_pcalloc(p, sizeof(aos_curl_http_transport_t));  
    
    t->pool = p;     
    t->options = aos_default_http_transport_options;      
    t->cleanup = aos_fstack_create(p, 5);  
   
    func.func1 = (aos_func1_pt)aos_transport_cleanup;      
    aos_fstack_push(t->cleanup, t, func, 1);  
     
    t->curl = aos_request_get();      
    func.func1 = (aos_func1_pt)request_release;      
    aos_fstack_push(t->cleanup, t->curl, func, 1);  
     
    t->header_callback = aos_curl_default_header_callback;    
    t->read_callback = aos_curl_default_read_callback;     
    t->write_callback = aos_curl_default_write_callback;      
 
 
    return (aos_http_transport_t *)t;  
} 
 
size_t aos_curl_default_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata){ 
    int len;    int bytes; 
    aos_curl_http_transport_t *t; 
 
    t = (aos_curl_http_transport_t *)(userdata);    
    len = size * nmemb; 
 
    if (t->controller->first_byte_time == 0) { 
        t->controller->first_byte_time = apr_time_now();   
    } 
 
    aos_curl_transport_headers_done(t); 
 
    if (t->controller->error_code != AOSE_OK) { 
        aos_debug_log(“write callback abort”);         
        return 0; 
    } 
 
    if (t->resp->status < 200 || t->resp->status > 299) { 
        bytes = aos_write_http_body_memory(t->resp, ptr, len);         
        assert(bytes == len); 
        aos_move_transport_state(t, TRANS_STATE_BODY_IN);        
        return bytes; 
    } 
 
    if (t->resp->type == BODY_IN_MEMORY && t->resp->body_len >= (int64_t)t->controller->options->max_memory_size) {         
         t->controller->reason = apr_psprintf(t->pool,  
              “receive body too big, current body size: %” APR_INT64_T_FMT “, max memory size: %” APR_INT64_T_FMT,  t->resp->body_len, t->controller->options->max_memory_size); 
        t->controller->error_code = AOSE_OVER_MEMORY; 
        aos_error_log(“error reason:%s, “, t->controller->reason); 
        return 0; 
    } 
    if ((bytes = t->resp->write_body(t->resp, ptr, len)) < 0) { 
        aos_debug_log(“write body failure, %d.”, bytes); 
        t->controller->error_code = AOSE_WRITE_BODY_ERROR; 
        t->controller->reason = “write body failure.”; 
        return 0; 
    } 
    aos_move_transport_state(t, TRANS_STATE_BODY_IN); 
    return bytes; 
} 
 
int aos_curl_transport_setup(aos_curl_http_transport_t *t) { 
     … 
     curl_easy_setopt_safe(CURLOPT_WRITEFUNCTION, t->write_callback); 
    … 
} 

紅色部分基本說明了oss c sdk如何呼叫CURL提供的CURLOPT_WRITEFUNCTION了。

       參考OSS C SDK使用CURL進行資料上傳和下載的實現,能夠根據自己的需求自定義資料上傳和下載的Callback,對資料進行一些預處理。  


相關文章