
# 通过自定义域名访问OBS(C SDK)
![](https://support.huaweicloud.com/sdk-c-devg-obs/public_sys-resources/notice_3.0-zh-cn.png)
开发过程中，您有任何问题可以在GitHub上[提交issue](https://github.com/huaweicloud/huaweicloud-sdk-c-obs/issues)，或者在[华为云对象存储服务论坛](https://bbs.huaweicloud.com/forum/forum-620-1.html)中发帖求助。
使用自定义域名访问服务端之前，需要先在console界面配置自定义域名。
#### 自定义域名访问介绍与配置
若需通过自定义域名访问OBS桶，须先将该域名绑定至OBS桶的访问域名[自定义域名绑定简介](https://support.huaweicloud.com/usermanual-obs/obs_03_0032.html)，[自定义域名绑定配置](https://support.huaweicloud.com/usermanual-obs/obs_03_0032.html)。
![](https://support.huaweicloud.com/sdk-c-devg-obs/public_sys-resources/caution_3.0-zh-cn.png)
当在自定义域名上配置了CDN加速服务，即自定义域名为CDN服务的加速域名时，需要额外对CDN服务进行配置，以保证可以正常使用自定义域名访问OBS服务。
以华为云CDN服务为例，相关配置如下所示：
1. 登录华为云CDN服务，从CDN服务左侧列表中选择域名管理项，在该项中可以查看到所有配置的CDN服务域名信息。
2. 配置源站。单击要使用的自定义域名项，进入域名配置界面，编辑源站配置，选择主源站类型为源站域名类型，对应源站为要访问的OBS桶域名。 
   ![](https://support.huaweicloud.com/sdk-c-devg-obs/zh-cn_image_0000001880248921.png "点击放大")
   
   
3. 配置回源HOST。回源HOST必须指定为加速域名即访问OBS服务时访问的自定义域名，否则可能会出现回源鉴权失败的问题。 
   ![](https://support.huaweicloud.com/sdk-c-devg-obs/zh-cn_image_0000001833289840.png "点击放大")
   
   
 
通过SDK，使用自定义域名访问OBS，可以完全复用对应接口的示例，只需要注意在设置option的时候，按如下方式设置：
```
option.bucket_options.useCname = true;
option.bucket_options.host_name = "yourCustomDomain";
```
#### 代码示例一：通过自定义域名上传对象
以下示例展示如何通过自定义域名上传对象：
```
#include "eSDKOBS.h"
#include <stdio.h>
#include <sys/stat.h>
// 响应回调函数，可以在这个回调中把properties的内容记录到callback_data(用户自定义回调数据)中
obs_status response_properties_callback(const obs_response_properties *properties, void *callback_data);
int put_file_data_callback(int buffer_size, char *buffer,
    void *callback_data);
void put_file_complete_callback(obs_status status,
    const obs_error_details *error,
    void *callback_data);
typedef struct put_file_object_callback_data
{
    FILE *infile;
    uint64_t content_length;
    obs_status ret_status;
} put_file_object_callback_data;
uint64_t open_file_and_get_length(char *localfile, put_file_object_callback_data *data);
int main()
{
    // 以下示例展示如何在使用自定义域名时通过put_object函数上传本地文件：
    // 在程序入口调用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");
    char * bucket_name = "";
    options.bucket_options.bucket_name = bucket_name;
    // 设置自定义域名
    options.bucket_options.useCname = true;
    options.bucket_options.host_name = "example.obs.test.cname.com";
    // 初始化上传对象属性
    obs_put_properties put_properties;
    init_put_properties(&put_properties);
    // 上传对象名
    char *key = "example_put_file_test";
    // 上传的文件
    char file_name[256] = "./example_local_file_test.txt";
    uint64_t content_length = 0;
    // 初始化存储上传数据的结构体
    put_file_object_callback_data data;
    memset(&data, 0, sizeof(put_file_object_callback_data));
    // 打开文件，并获取文件长度
    content_length = open_file_and_get_length(file_name, &data);
    // 设置回调函数
    obs_put_object_handler putobjectHandler =
    {
        { &response_properties_callback, &put_file_complete_callback },
        &put_file_data_callback
    };
    put_object(&options, key, content_length, &put_properties, 0, &putobjectHandler, &data);
    if (OBS_STATUS_OK == data.ret_status) {
        printf("put object from file successfully. \n");
    }
    else
    {
        printf("put object failed(%s).\n",
            obs_get_status_name(data.ret_status));
    }
    if (data.infile != NULL) {
        fclose(data.infile);
    }
    // 释放分配的全局资源
    obs_deinitialize();
}
// 响应回调函数，可以在这个回调中把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;
}
int put_file_data_callback(int buffer_size, char *buffer,
    void *callback_data)
{
    put_file_object_callback_data *data =
        (put_file_object_callback_data *)callback_data;
    int ret = 0;
    if (data->content_length) {
        int toRead = ((data->content_length > (unsigned)buffer_size) ?
            (unsigned)buffer_size : data->content_length);
        ret = fread(buffer, 1, toRead, data->infile);
    }
    uint64_t originalContentLength = data->content_length;
    data->content_length -= ret;
    if (data->content_length) {
        printf("%llu bytes remaining ", (unsigned long long)data->content_length);
        printf("(%d%% complete) ...\n",
            (int)(((originalContentLength - data->content_length) * 100) / originalContentLength));
    }
    return ret;
}
void put_file_complete_callback(obs_status status,
    const obs_error_details *error,
    void *callback_data)
{
    put_file_object_callback_data *data = (put_file_object_callback_data *)callback_data;
    data->ret_status = status;
}
uint64_t open_file_and_get_length(char *localfile, put_file_object_callback_data *data)
{
    uint64_t content_length = 0;
    if (!content_length)
    {
        struct stat statbuf;
        if (stat(localfile, &statbuf) == -1)
        {
            fprintf(stderr, "\nERROR: Failed to stat file %s: ",
                localfile);
            return 0;
        }
        content_length = statbuf.st_size;
    }
    if (!(data->infile = fopen(localfile, "rb")))
    {
        fprintf(stderr, "\nERROR: Failed to open input file %s: ",
            localfile);
        return 0;
    }
    data->content_length = content_length;
    return content_length;
}
```
#### 代码示例二：通过自定义域名下载对象
以下示例展示如何通过自定义域名下载对象：
```
#include "eSDKOBS.h"
#include <stdio.h>
#include <sys/stat.h>
// 响应回调函数，可以在这个回调中把properties的内容记录到callback_data(用户自定义回调数据)中
obs_status response_properties_callback(const obs_response_properties *properties, void *callback_data);
obs_status get_object_data_callback(int buffer_size, const char *buffer,
    void *callback_data);
void get_object_complete_callback(obs_status status,
    const obs_error_details *error,
    void *callback_data);
typedef struct get_object_callback_data
{
    FILE *outfile;
    obs_status ret_status;
}get_object_callback_data;
FILE * write_to_file(char *localfile);
int main()
{
    // 以下示例展示如何在使用自定义域名时通过get_object函数下载对象到本地文件：
    // 在程序入口调用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");
    char * bucket_name = "";
    options.bucket_options.bucket_name = bucket_name;
    // 设置自定义域名
    options.bucket_options.useCname = true;
    options.bucket_options.host_name = "example.obs.test.cname.com";
    // 设置对象下载到本地的文件名
    char *file_name = "./example_get_file_test";
    obs_object_info object_info;
    // 设置下载的对象
    memset(&object_info, 0, sizeof(obs_object_info));
    object_info.key = "example_get_file_test";
    object_info.version_id = NULL;
    //根据业务需要设置存放下载对象数据的结构
    get_object_callback_data data;
    data.ret_status = OBS_STATUS_BUTT;
    data.outfile = write_to_file(file_name);
    // 定义范围下载参数
    obs_get_conditions getcondition;
    memset(&getcondition, 0, sizeof(obs_get_conditions));
    init_get_properties(&getcondition);
    // 下载起始位置，默认0，从对象0字节位置开始下载
    getcondition.start_byte = 0;
    // 下载长度，默认0，一直下载到对象尾
    // getcondition.byte_count = 0;
    // 定义下载的回调函数
    obs_get_object_handler get_object_handler =
    {
        { &response_properties_callback,
          &get_object_complete_callback},
        &get_object_data_callback
    };
    get_object(&options, &object_info, &getcondition, 0, &get_object_handler, &data);
    if (OBS_STATUS_OK == data.ret_status) {
        printf("get object successfully. \n");
    }
    else
    {
        printf("get object failed(%s).\n", obs_get_status_name(data.ret_status));
    }
    fclose(data.outfile);
    // 释放分配的全局资源
    obs_deinitialize();
}
// 响应回调函数，可以在这个回调中把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 get_object_data_callback(int buffer_size, const char *buffer,
    void *callback_data)
{
    get_object_callback_data *data = (get_object_callback_data *)callback_data;
    size_t wrote = fwrite(buffer, 1, buffer_size, data->outfile);
    return ((wrote < (size_t)buffer_size) ?
        OBS_STATUS_AbortedByCallback : OBS_STATUS_OK);
}
void get_object_complete_callback(obs_status status,
    const obs_error_details *error,
    void *callback_data)
{
    get_object_callback_data *data = (get_object_callback_data *)callback_data;
    data->ret_status = status;
}
FILE * write_to_file(char *localfile)
{
    FILE *outfile = 0;
    if (localfile) {
        struct stat buf;
        if (stat(localfile, &buf) == -1) {
            outfile = fopen(localfile, "wb");
        }
        else {
            outfile = fopen(localfile, "a");
        }
        if (!outfile) {
            fprintf(stderr, "\nERROR: Failed to open output file %s: ",
                localfile);
            return outfile;
        }
    } else {
        fprintf(stderr, "\nERROR: Failed to open output file, it's NULL, write object to stdout.",
            localfile);
        outfile = stdout;
    }
    return outfile;
}
```
