本文最后更新于 2023-07-07,文章内容可能已经过时。

官方文档:https://cloud.tencent.com/document/product/436/7749

// 先声明所需的方法
void get_cos_text(char *upload_host, char *object_name, char *upload_secret_id, char *upload_secret_key, char *upload_authorization);
int upload_text(char *filepath, char *upload_host, char *object_name, char *upload_authorization);
char* iso8601(char expire_time[32]);
void quote(char *source, char *target);
void sha1Hex(char *str, char *newString);
 
/**
 * 上传文件到cos桶中
 * 先获取文件名和拼接存储路径,再获取时间拼接成申请单,之后根据cos桶名和地区拼接cos地址,获取上传许可,上传
 * filepath 上传文件的绝对路径
 * log_secret_id cos平台的secret_id
 * log_secret_key cos平台的secret_key
 * log_bucket_name_appid cos平台的bucket_name_appid
 * log_upload_region cos平台的upload_region
*/
int upload_log(char *filepath, char *SECRET_ID, char *SECRET_KEY, char * bucket_name_appid,char *region)
{
    logger(6, LOG_SERVICE, "*********************开始上传文件,filepath=%s",filepath);
    if (access(filepath, 0) != 0) {
        logger(3, LOG_SERVICE, "文件不存在,filepath=%s", filepath);
        return -1;
    }
    // 文件名称,filename获取认证时使用,objectname为上传时作为存储桶里的绝对路径
    char *filename = strrchr(filepath, '/') +1;
    logger(6, LOG_SERVICE, "filename=%s\n", filename);
    char device_id[32] = {0};
    get_device_id(device_id);
    logger(6, LOG_SERVICE, "device_id=%s\n", device_id);
    char objectname[100] = {0};
    sprintf(objectname, "/%s/%s", device_id,filename );
    logger(6, LOG_SERVICE, "objectname=%s\n", objectname);
 
    // 文件过期时间
    char expire_time[32] = {0};
    iso8601(expire_time);
    char apply_payload[156] = {0};
    sprintf(apply_payload, "{\"MediaType\": \"TEXT\",  \"MediaName\": \"%s\", \"ExpireTime\": \"%s\"}", filename, expire_time);
    logger(6, LOG_SERVICE, "apply_payload=%s\n", apply_payload);
 
    int ret = -1;
 
    // 2. 拼接上传的cos地址
    char upload_host[100] = {0};
    sprintf(upload_host, "%s.cos.%s.myqcloud.com", bucket_name_appid, region);
    logger(6, LOG_SERVICE, "upload_host=%s\n", upload_host);
 
    char upload_authorization[512] = {0};
    get_cos_text(upload_host, objectname, SECRET_ID, SECRET_KEY, upload_authorization);
    logger(6, LOG_SERVICE, "upload_suthorization=%s", upload_authorization);
    ret = upload_text(filepath, upload_host, objectname,  upload_authorization);
    logger(6, LOG_SERVICE, "ret = %d", ret);
    if (ret == 0)
    {
        logger(6, LOG_SERVICE, "上传文件%s成功!",objectname);
    } else {
        logger(6, LOG_SERVICE, "上传文件%s失败!",objectname);
    }
    return ret;
}
 
 
void get_cos_text(char *upload_host, char *object_name, char *upload_secret_id, char *upload_secret_key, char *upload_authorization)
{
    // 1. 生成keytime(表示签名有效时长)
    long start_timestamp = time(NULL);
    long end_timestamp = start_timestamp + 6000;
    char key_time[32] = {0};
    sprintf(key_time, "%ld;%ld", start_timestamp, end_timestamp);
    logger(6, LOG_SERVICE, "key_time%s",key_time);
 
    // 2. 生成SignKey(使用HMAC函数生成消息摘要,函数需要指定一个散列函数,一个密钥,一个消息,一个接收的变量,之后将其通过HexEncode转换为十六进制)
    unsigned char sSHA[20] = {0};
    unsigned int nSHALen = 20;
    HMAC(EVP_sha1(), upload_secret_key, strlen(upload_secret_key), (const unsigned char*)key_time, strlen(key_time), sSHA, &nSHALen);
    char sign_key[40] = {0};
    HexEncode(sSHA, sign_key, 20);
    logger(6, LOG_SERVICE, "sign_key%s",sign_key);
 
    // 3. 生成 UrlParamList 和 HttpParameters
    // char *urlParamList="";
    // char *httpParameters="";
 
    // 4. 生成 HeaderList 和 HttpHeaders(header_list是传入的参数名称,HttpHeaders是参数名称和值,里面的符号需要转义)
    char *header_list = "content-type;date;host";
 
    char http_headers[1300] = {0};
    char *content_type = "content-type=text/plain";
    char encoded_content_type[30] = {0};
    struct tm *sttime;
 
    quote(content_type, encoded_content_type);
    sttime = localtime(&start_timestamp);
    char encoded_date[50] = {0};
    strftime(upload_date, sizeof(upload_date), "%a, %d %b %Y %H:%M:%S GMT", sttime);
    quote(upload_date, encoded_date);
    sprintf(http_headers, "%s&date=%s&host=%s", encoded_content_type, encoded_date, upload_host);
    logger(6, LOG_SERVICE, "eaderList 和 HttpHeaders");
 
    // 5. 生成 HttpString
    char http_string[1500] = {0};
    sprintf(http_string, "put\n%s\n%s\n%s\n", object_name, "", http_headers);
    char hashed_http_string[64] = {0};
    sha1Hex(http_string, hashed_http_string);
    logger(6, LOG_SERVICE, "HttpString");
 
    // 6. 生成 StringToSign,使用 SHA1 对 HttpString 计算的消息摘要,16进制小写形式,用到第五步的HttpString
    char string_to_sign[100] = {0};
    sprintf(string_to_sign, "sha1\n%s\n%s\n", key_time, hashed_http_string);
    logger(6, LOG_SERVICE, "StringToSign");
 
    // 7. 生成 Signature,用到第二步的signkey,第六步的StringToSign
    unsigned char skShaSign[20] = {0};
    HMAC(EVP_sha1(), sign_key, strlen(sign_key), (const unsigned char*)string_to_sign, strlen(string_to_sign), skShaSign, &nSHALen);
    char signature[40] = {0};
    HexEncode(skShaSign, signature, 20);
    logger(6, LOG_SERVICE, "Signature");
 
    // 8.生成签名,拼接好的签名赋值给upload_authorization
    sprintf(upload_authorization, "q-sign-algorithm=sha1&q-ak=%s&q-sign-time=%s&q-key-time=%s&q-header-list=%s&q-url-param-list=%s&q-signature=%s", upload_secret_id, key_time, key_time, header_list, "", signature);
    logger(6, LOG_SERVICE, "upload_authorization");
}
 
int upload_text(char *filepath, char *upload_host, char *object_name, char *upload_authorization)
{
     int ret = -1;
 
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    if (curl)
    {
      // 拼接发送目标url
        char upload_url[256] = {0};
        sprintf(upload_url, "https://%s%s", upload_host, object_name);
        logger(6, LOG_SERVICE, "upload_url=%s", upload_url);
 
      // 拼接头部header
        struct curl_slist *upload_headers = NULL;
        char upload_header_item[1500] = {0};
        sprintf(upload_header_item, "Host: %s", upload_host);
        upload_headers = curl_slist_append(upload_headers, upload_header_item);
 
        upload_headers = curl_slist_append(upload_headers, "Content-Type: text/plain");
 
 
        sprintf(upload_header_item, "Authorization: %s", upload_authorization);
        upload_headers = curl_slist_append(upload_headers, upload_header_item);
 
        sprintf(upload_header_item, "Date: %s", upload_date);
        upload_headers = curl_slist_append(upload_headers, upload_header_item);
 
       // 读取文件
        FILE *hd_src;
        hd_src = fopen(filepath, "rb");
         
      // 拼接上传请求
        curl_easy_setopt(curl, CURLOPT_URL, upload_url);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);   // 关闭证书认证
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        curl_easy_setopt(curl, CURLOPT_PUT, 1L);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, upload_headers);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, upload_read_callback);
        curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, upload_write_callback);
         
      // 发起上传请求
        res = curl_easy_perform(curl);
 
        if (res != CURLE_OK) {
            logger(3, LOG_SERVICE, "上传文件执行curl失败!1");
        } else {
            long respCode = 0;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &respCode);
            logger(6, LOG_SERVICE, "上传文件执行curl成功,responseCode=%ld", respCode);
            logger(6, LOG_SERVICE, "uploadResponse=%s\n", upload_response);
            if (respCode == 200) {
                ret = 0;
            }
        }
        fclose(hd_src);
        // clean
        curl_easy_cleanup(curl);
        curl_slist_free_all(upload_headers);
    } else {
        logger(3, LOG_SERVICE, "上传curl初始化失败!2");
    }
 
    return ret;
}
 
 
char* iso8601(char expire_time[32])
{
    time_t t_time, after;
    struct tm *t_tm;
 
    t_time = time(NULL);
    after = t_time + 3600*24*30*2;
    t_tm = localtime(&after);
    if (t_tm == NULL)
        return NULL;
 
    if(strftime(expire_time, 32, "%FT%T%z", t_tm) == 0)
        return NULL;
                                 
    expire_time[25] = expire_time[24];
    expire_time[24] = expire_time[23];
    expire_time[22] = ':';
    expire_time[26] = '\0';
    return expire_time;
}
 
 
void HexEncode(const unsigned char* input, char* output, int len)
{
    static const char* const lut = "0123456789abcdef";
    char addOutStr[128] = {0};
    char tempStr[2] = {0};
    size_t i;
    for (i = 0; i < len; ++i)
    {
        const unsigned char c = input[i];
        tempStr[0] = lut[c >> 4];
        strcat(addOutStr, tempStr);
        tempStr[0] = lut[c & 15];
        strcat(addOutStr, tempStr);
    }
    strncpy(output, addOutStr, 128);
}
 
void quote(char *source, char *target)
{
    // Tue, 10 Jan 2023 06:44:07 GMT
    // Tue%2C%2010%20Jan%202023%2006%3A44%3A07%20GMT
    int i;
    int j=0;
    for(i = 0; source[i] != '\0'; i++)
    {
        if(source[i] != ' ' && source[i] != ',' && source[i] != ':' && source[i] != '/')
        {
            target[j++] = source[i];
        }
        else if (source[i] == ' ')
        {
            target[j++] = '%';
            target[j++] = '2';
            target[j++] = '0';
        }
        else if(source[i] == ',')
        {
            target[j++] = '%';
            target[j++] = '2';
            target[j++] = 'C';
        }
        else if(source[i] == ':')
        {
            target[j++] = '%';
            target[j++] = '3';
            target[j++] = 'A';
        }
        else if(source[i] == '/')
        {
            target[j++] = '%';
            target[j++] = '2';
            target[j++] = 'F';
        }
    }
    target[j] = '\0';
}
 
void sha1Hex(char *str, char *newString)
{
    char buf[3];
    unsigned char hash[SHA_DIGEST_LENGTH];
    SHA_CTX sha1;
    SHA1_Init(&sha1);
    SHA1_Update(&sha1, str, strlen(str));
    SHA1_Final(hash, &sha1);
    int i;
    for(i = 0; i < SHA_DIGEST_LENGTH; i++)
    {
        snprintf(buf, sizeof(buf), "%02x", hash[i]);
        strcat(newString, buf);
    }
}