文档首页/ 对象存储服务 OBS/ SDK参考/ C/ 桶相关接口(C SDK)/ 其他接口(C SDK)/ 使用自定义头域(Custom Headers)(C SDK)
更新时间:2026-09-08 GMT+08:00
分享

使用自定义头域(Custom Headers)(C SDK)

开发过程中,您有任何问题可以在GitHub上提交issue,或者在华为云对象存储服务论坛中发帖求助。

功能说明

OBS C SDK 支持在发起请求时携带自定义 HTTP 头域。通过 obs_options.request_options 中的 custom_headers 字段设置,所有操作(PUT/GET/DELETE/LIST 等)均支持。

接口约束

头域前缀

大小写处理

是否参与签名

x-obs-*

自动转小写

是

其他

保持原样

否

  • 以 x-obs- 开头的头域名称会自动转为小写,并参与请求签名计算
  • 其他头域保持原始大小写,仅作为 HTTP 头发送,不参与签名
  • Content-Type、Content-MD5 等标准签名头应通过 obs_put_properties 设置,不要通过自定义头域传入

方法定义

表1 obs_http_request_option

参数名称

参数类型

是否必选

描述

ustom_headers_count

int

否

参数解释:

自定义头域的数量。

约束限制:

自定义头域与用户元数据(meta_data)共享内部数组。

取值范围:

≥ 0 的整数。为 0 时不发送自定义头域;为负数时等同于 0。

默认取值:

0

custom_headers

表2 obs_name_value*

否

参数解释:

自定义头域数组,每个元素为一个表2 obs_name_value 键值对。

约束限制:

当 custom_headers_count > 0 时不能为 NULL;每个元素的 name 和 value 不能为 NULL,否则该条目被跳过。所有头域的原始字节总长度不超过 OBS_MAX_METADATA_SIZE(4096 字节),超出返回 OBS_STATUS_MetadataHeadersTooLong。不要通过此字段设置 Content-Type、Content-MD5 等标准签名头,应通过 obs_put_properties 对应字段设置,否则可能导致签名不一致。

取值范围:

指向 obs_name_value 数组的指针,数组长度须 ≥ custom_headers_count。

默认取值:

NULL

表2 obs_name_value

参数名称

参数类型

是否必选

描述

name

char *

是

参数解释:

自定义头域的名称。

约束限制:

不能为 NULL,否则该条目被跳过。

取值范围:

合法的 HTTP 头域名称字符串。以 x-obs- 开头的名称会自动转小写并参与签名;其他名称保持原样不参与签名。

默认取值:

无

value

char *

是

参数解释:

自定义头域的值。

约束限制:

不能为 NULL,否则该条目被跳过。指针在请求完成前必须保持有效。

取值范围:

合法的 HTTP 头域值字符串。

默认取值:

无

代码示例一:上传对象时携带自定义头域

以下示例展示如何上传对象时携带自定义头域:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include "eSDKOBS.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 响应回调函数,可以在这个回调中把properties的内容记录到callback_data(用户自定义回调数据)中
obs_status response_properties_callback(const obs_response_properties *properties, void *callback_data);
// 结束回调函数,可以在这个回调中把obs_status和obs_error_details的内容记录到callback_data(用户自定义回调数据)中
void response_complete_callback(obs_status status, const obs_error_details *error, void *callback_data);
// 上传对象的数据回调函数
int put_object_data_callback(int buffer_size, char *buffer, void *callback_data);
// 自定义回调数据:用于在回调间传递上传内容和结果状态
typedef struct PutObjectData
{
    char *data;
    uint64_t data_len;
    uint64_t data_read;
    obs_status ret_status;
} PutObjectData;
int main()
{
    // 以下示例展示如何上传对象时携带自定义头域:
    // 在程序入口调用obs_initialize方法来初始化网络、内存等全局资源。
    obs_status ret_status = obs_initialize(OBS_INIT_ALL);
    if (OBS_STATUS_OK != ret_status)
    {
        printf("obs_initialize failed(%s).\n", obs_get_status_name(ret_status));
        return -1;
    }
    obs_options options;
    // 创建并初始化options,该参数包括访问域名(host_name)、访问密钥(access_key_id和access_key_secret)、桶名(bucket_name)、桶存储类别(storage_class)等配置信息
    init_obs_options(&options);
    // host_name填写桶所在的endpoint, 此处以华北-北京四为例,其他地区请按实际情况填写。
    options.bucket_options.host_name = "obs.cn-north-4.myhuaweicloud.com";
    
    
    
    
    
    
    // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
    // 本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量ACCESS_KEY_ID和SECRET_ACCESS_KEY。
    options.bucket_options.access_key = getenv("ACCESS_KEY_ID");
    options.bucket_options.secret_access_key = getenv("SECRET_ACCESS_KEY");
    if (!options.bucket_options.access_key || !options.bucket_options.secret_access_key) {
        printf("ERROR: ACCESS_KEY_ID and SECRET_ACCESS_KEY environment variables must be set.\n");
        obs_deinitialize();
        return -1;
    }
    // 填写Bucket名称,例如example-bucket-name。
    char * bucket_name = "example-bucket-name";
    options.bucket_options.bucket_name = bucket_name;
    // 设置自定义头域(x-obs-前缀的头会自动参与签名,其余头域仅发送不签名)
    obs_name_value headers[2];
    memset(headers, 0, sizeof(headers));
    headers[0].name = "x-obs-request-source";
    headers[0].value = "sdk-custom-header";
    headers[1].name = "X-Client-Version";
    headers[1].value = "1.0.0";
    options.request_options.custom_headers = headers;
    options.request_options.custom_headers_count = 2;
    // 设置上传属性
    char *key = "test-object.txt";
    char *data = "Hello OBS!";
    obs_put_properties put_properties;
    init_put_properties(&put_properties);
    put_properties.content_type = "text/plain";
    // 构造回调数据与回调结构体
    PutObjectData putData;
    memset(&putData, 0, sizeof(PutObjectData));
    putData.data = data;
    putData.data_len = strlen(data);
    putData.ret_status = OBS_STATUS_BUTT;
    obs_put_object_handler handler =
    {
        {&response_properties_callback, &response_complete_callback},
        &put_object_data_callback,
        NULL
    };
    // 上传对象(自定义头域会自动携带)
    put_object(&options, key, strlen(data), &put_properties, NULL, &handler, &putData);
    // 判断请求是否成功
    if (putData.ret_status == OBS_STATUS_OK) {
        printf("put object successfully. \n");
    }
    else
    {
        printf("put object failed(%s).\n", obs_get_status_name(putData.ret_status));
    }
    // 释放分配的全局资源
    obs_deinitialize();
}
// 上传对象的数据回调函数,SDK通过它读取待上传的数据
int put_object_data_callback(int buffer_size, char *buffer, void *callback_data)
{
    PutObjectData *putData = (PutObjectData *)callback_data;
    if (putData == NULL) {
        return 0;
    }
    uint64_t remaining = putData->data_len - putData->data_read;
    if (remaining <= 0) {
        return 0;
    }
    int toRead = (remaining < (uint64_t)buffer_size) ? (int)remaining : buffer_size;
    memcpy(buffer, putData->data + putData->data_read, toRead);
    putData->data_read += toRead;
    return toRead;
}
// 响应回调函数,可以在这个回调中把properties的内容记录到callback_data(用户自定义回调数据)中
obs_status response_properties_callback(const obs_response_properties *properties, void *callback_data)
{
    if (properties == NULL)
    {
        printf("error! obs_response_properties is null!");
        return OBS_STATUS_OK;
    }
// 打印响应信息
#define print_nonnull(name, field)                                 \
    do {                                                           \
        if (properties-> field) {                                  \
            printf("%s: %s\n", name, properties->field);          \
        }                                                          \
    } while (0)
    print_nonnull("request_id", request_id);
    print_nonnull("request_id2", request_id2);
    print_nonnull("content_type", content_type);
    if (properties->content_length) {
        printf("content_length: %llu\n", properties->content_length);
    }
    print_nonnull("server", server);
    print_nonnull("ETag", etag);
    print_nonnull("expiration", expiration);
    print_nonnull("website_redirect_location", website_redirect_location);
    print_nonnull("version_id", version_id);
    print_nonnull("allow_origin", allow_origin);
    print_nonnull("allow_headers", allow_headers);
    print_nonnull("max_age", max_age);
    print_nonnull("allow_methods", allow_methods);
    print_nonnull("expose_headers", expose_headers);
    print_nonnull("storage_class", storage_class);
    print_nonnull("server_side_encryption", server_side_encryption);
    print_nonnull("kms_key_id", kms_key_id);
    print_nonnull("customer_algorithm", customer_algorithm);
    print_nonnull("customer_key_md5", customer_key_md5);
    print_nonnull("bucket_location", bucket_location);
    print_nonnull("obs_version", obs_version);
    print_nonnull("restore", restore);
    print_nonnull("obs_object_type", obs_object_type);
    print_nonnull("obs_next_append_position", obs_next_append_position);
    print_nonnull("obs_head_epid", obs_head_epid);
    print_nonnull("reserved_indicator", reserved_indicator);
    int i;
    for (i = 0; i < properties->meta_data_count; i++) {
        printf("x-obs-meta-%s: %s\n", properties->meta_data[i].name,
            properties->meta_data[i].value);
    }
    return OBS_STATUS_OK;
}
// 结束回调函数,可以在这个回调中把obs_status和obs_error_details的内容记录到callback_data(用户自定义回调数据)中
void response_complete_callback(obs_status status, const obs_error_details *error, void *callback_data)
{
    if (callback_data) {
        PutObjectData *putData = (PutObjectData *)callback_data;
        putData->ret_status = status;
    } else {
        printf("Callback_data is NULL");
    }
    if (error && error->message) {
        printf("Error Message: \n   %s\n", error->message);
    }
    if (error && error->resource) {
        printf("Error Resource: \n  %s\n", error->resource);
    }
    if (error && error->further_details) {
        printf("Error further_details: \n   %s\n", error->further_details);
    }
    if (error && error->extra_details_count) {
        int i;
        for (i = 0; i < error->extra_details_count; i++) {
            printf("Error Extra Detail(%d):\n   %s:%s\n", i, error->extra_details[i].name,
                   error->extra_details[i].value);
        }
    }
    if (error && error->error_headers_count) {
        int i;
        for (i = 0; i < error->error_headers_count; i++) {
            const char *errorHeader = error->error_headers[i];
            printf("Error Headers(%d):\n    %s\n", i, errorHeader == NULL ? "NULL Header" : errorHeader);
        }
    }
}

该请求发出的 HTTP 头将包含:

x-obs-request-source: sdk-custom-header    <- 参与签名(名称已转小写)
X-Client-Version: 1.0.0                    <- 不参与签名(保持原始大小写)

代码示例二:下载对象时携带自定义头域

#include "eSDKOBS.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 响应回调函数,可以在这个回调中把properties的内容记录到callback_data(用户自定义回调数据)中
obs_status response_properties_callback(const obs_response_properties *properties, void *callback_data);
// 结束回调函数,可以在这个回调中把obs_status和obs_error_details的内容记录到callback_data(用户自定义回调数据)中
void response_complete_callback(obs_status status, const obs_error_details *error, void *callback_data);
// 下载对象的数据回调函数,SDK通过它返回下载的对象内容
obs_status get_object_data_callback(int buffer_size, const char *buffer, void *callback_data);
// 自定义回调数据:用于在回调间传递下载内容和结果状态
typedef struct GetObjectData
{
    FILE *fp;
    obs_status ret_status;
} GetObjectData;
int main()
{
    // 以下示例展示如何下载对象时携带自定义头域:
    // 在程序入口调用obs_initialize方法来初始化网络、内存等全局资源。
    obs_status ret_status = obs_initialize(OBS_INIT_ALL);
    if (OBS_STATUS_OK != ret_status)
    {
        printf("obs_initialize failed(%s).\n", obs_get_status_name(ret_status));
        return -1;
    }
    obs_options options;
    // 创建并初始化options,该参数包括访问域名(host_name)、访问密钥(access_key_id和access_key_secret)、桶名(bucket_name)、桶存储类别(storage_class)等配置信息
    init_obs_options(&options);
    // host_name填写桶所在的endpoint, 此处以华北-北京四为例,其他地区请按实际情况填写。
    options.bucket_options.host_name = "obs.cn-north-4.myhuaweicloud.com";
    // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
    // 本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量ACCESS_KEY_ID和SECRET_ACCESS_KEY。
    options.bucket_options.access_key = getenv("ACCESS_KEY_ID");
    options.bucket_options.secret_access_key = getenv("SECRET_ACCESS_KEY");
    if (!options.bucket_options.access_key || !options.bucket_options.secret_access_key) {
        printf("ERROR: ACCESS_KEY_ID and SECRET_ACCESS_KEY environment variables must be set.\n");
        obs_deinitialize();
        return -1;
    }
    // 填写Bucket名称,例如example-bucket-name。
    char * bucket_name = "example-bucket-name";
    options.bucket_options.bucket_name = bucket_name;
    // 设置自定义头域(x-obs-前缀的头会自动参与签名,其余头域仅发送不签名)
    obs_name_value headers[1];
    memset(headers, 0, sizeof(headers));
    headers[0].name = "x-obs-request-source";
    headers[0].value = "download-with-custom-header";
    options.request_options.custom_headers = headers;
    options.request_options.custom_headers_count = 1;
    // 设置下载对象信息
    char *key = "test-object.txt";
    obs_object_info object_info;
    memset(&object_info, 0, sizeof(obs_object_info));
    object_info.key = key;
    object_info.version_id = NULL;
    // 构造回调数据与回调结构体
    GetObjectData getData;
    memset(&getData, 0, sizeof(GetObjectData));
    getData.fp = fopen("test-object.txt", "wb");
    if (getData.fp == NULL) {
        printf("ERROR: failed to open local file for writing.\n");
        obs_deinitialize();
        return -1;
    }
    getData.ret_status = OBS_STATUS_BUTT;
    obs_get_object_handler handler =
    {
        {&response_properties_callback, &response_complete_callback},
        &get_object_data_callback,
        NULL
    };
    // 下载对象(自定义头域会自动携带)
    get_object(&options, &object_info, NULL, NULL, &handler, &getData);
    fclose(getData.fp);
    // 判断请求是否成功
    if (getData.ret_status == OBS_STATUS_OK) {
        printf("get object successfully. \n");
    }
    else
    {
        printf("get object failed(%s).\n", obs_get_status_name(getData.ret_status));
    }
    // 释放分配的全局资源
    obs_deinitialize();
}
// 下载对象的数据回调函数,SDK通过它返回下载的对象内容
obs_status get_object_data_callback(int buffer_size, const char *buffer, void *callback_data)
{
    GetObjectData *getData = (GetObjectData *)callback_data;
    if (getData == NULL || getData->fp == NULL) {
        return OBS_STATUS_OK;
    }
    if (buffer_size > 0 && buffer) {
        fwrite(buffer, 1, buffer_size, getData->fp);
    }
    return OBS_STATUS_OK;
}
// 响应回调函数,可以在这个回调中把properties的内容记录到callback_data(用户自定义回调数据)中
obs_status response_properties_callback(const obs_response_properties *properties, void *callback_data)
{
    if (properties == NULL)
    {
        printf("error! obs_response_properties is null!");
        return OBS_STATUS_OK;
    }
// 打印响应信息
#define print_nonnull(name, field)                                 \
    do {                                                           \
        if (properties-> field) {                                  \
            printf("%s: %s\n", name, properties->field);          \
        }                                                          \
    } while (0)
    print_nonnull("request_id", request_id);
    print_nonnull("request_id2", request_id2);
    print_nonnull("content_type", content_type);
    if (properties->content_length) {
        printf("content_length: %llu\n", properties->content_length);
    }
    print_nonnull("server", server);
    print_nonnull("ETag", etag);
    print_nonnull("expiration", expiration);
    print_nonnull("website_redirect_location", website_redirect_location);
    print_nonnull("version_id", version_id);
    print_nonnull("allow_origin", allow_origin);
    print_nonnull("allow_headers", allow_headers);
    print_nonnull("max_age", max_age);
    print_nonnull("allow_methods", allow_methods);
    print_nonnull("expose_headers", expose_headers);
    print_nonnull("storage_class", storage_class);
    print_nonnull("server_side_encryption", server_side_encryption);
    print_nonnull("kms_key_id", kms_key_id);
    print_nonnull("customer_algorithm", customer_algorithm);
    print_nonnull("customer_key_md5", customer_key_md5);
    print_nonnull("bucket_location", bucket_location);
    print_nonnull("obs_version", obs_version);
    print_nonnull("restore", restore);
    print_nonnull("obs_object_type", obs_object_type);
    print_nonnull("obs_next_append_position", obs_next_append_position);
    print_nonnull("obs_head_epid", obs_head_epid);
    print_nonnull("reserved_indicator", reserved_indicator);
    int i;
    for (i = 0; i < properties->meta_data_count; i++) {
        printf("x-obs-meta-%s: %s\n", properties->meta_data[i].name,
            properties->meta_data[i].value);
    }
    return OBS_STATUS_OK;
}
// 结束回调函数,可以在这个回调中把obs_status和obs_error_details的内容记录到callback_data(用户自定义回调数据)中
void response_complete_callback(obs_status status, const obs_error_details *error, void *callback_data)
{
    if (callback_data) {
        GetObjectData *getData = (GetObjectData *)callback_data;
        getData->ret_status = status;
    } else {
        printf("Callback_data is NULL");
    }
    if (error && error->message) {
        printf("Error Message: \n   %s\n", error->message);
    }
    if (error && error->resource) {
        printf("Error Resource: \n  %s\n", error->resource);
    }
    if (error && error->further_details) {
        printf("Error further_details: \n   %s\n", error->further_details);
    }
    if (error && error->extra_details_count) {
        int i;
        for (i = 0; i < error->extra_details_count; i++) {
            printf("Error Extra Detail(%d):\n   %s:%s\n", i, error->extra_details[i].name,
                   error->extra_details[i].value);
        }
    }
    if (error && error->error_headers_count) {
        int i;
        for (i = 0; i < error->error_headers_count; i++) {
            const char *errorHeader = error->error_headers[i];
            printf("Error Headers(%d):\n    %s\n", i, errorHeader == NULL ? "NULL Header" : errorHeader);
        }
    }
}

相关文档