
# 批量恢复归档或深度归档存储对象(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)中发帖求助。
#### 代码示例
本示例用于批量恢复归档或深度归档存储的对象。归档或深度归档类型的对象处于冷冻状态无法直接访问，需要先提交恢复请求，等待恢复完成后才能访问。
```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "eSDKOBS.h"
#define MAX_KEY_LEN     1024
#define MAX_TASK        10000
#define RESTORE_WAIT_SECONDS 6
// 单个对象恢复任务
typedef struct {
    char key[MAX_KEY_LEN];
    obs_status status;  
} restore_task;
// 批量恢复结果统计
typedef struct {
    int total;
    int success;
    int failed;
    char failed_keys[100][MAX_KEY_LEN];
    int failed_count;
    pthread_mutex_t mutex;
} batch_result;
static batch_result g_result = {0, 0, 0, {{0}}, 0, PTHREAD_MUTEX_INITIALIZER};
static restore_task *g_tasks = NULL;
static int g_task_count = 0;
static int g_task_index = 0;
static pthread_mutex_t g_task_mutex = PTHREAD_MUTEX_INITIALIZER;
// 响应回调函数
static obs_status restore_properties_callback(const obs_response_properties *properties, void *callback_data)
{
    (void)properties;
    (void)callback_data;
    return OBS_STATUS_OK;
}
// 结束回调函数
static void restore_complete_callback(obs_status status,
                                      const obs_error_details *error,
                                      void *callback_data)
{
    (void)error;
    restore_task *task = (restore_task *)callback_data;
    char *key = task ? task->key : "unknown";
    if (task) {                                                                                                                                                                    
        task->status = status;                                                                                                                                                     
    } 
    pthread_mutex_lock(&g_result.mutex);
    if (status == OBS_STATUS_OK) {
        g_result.success++;
        printf("[SUCCESS] restore %s\n", key);
    } else {
        g_result.failed++;
        if (g_result.failed_count < 100) {
            strncpy(g_result.failed_keys[g_result.failed_count], key, MAX_KEY_LEN - 1);
            g_result.failed_keys[g_result.failed_count][MAX_KEY_LEN - 1] = '\0';
            g_result.failed_count++;
        }
        printf("[FAILED] restore %s: %s\n", key, obs_get_status_name(status));
    }
    pthread_mutex_unlock(&g_result.mutex);
}
// 列举对象的回调数据
typedef struct {
    restore_task *tasks;
    int max_count;
    int current_count;
    obs_status ret_status;
} list_cb_data;
// 列举对象的数据回调
static obs_status list_objects_callback(int is_truncated, const char *next_marker,
                                        int contents_count,
                                        const obs_list_objects_content *contents,
                                        int common_prefixes_count,
                                        const char **common_prefixes,
                                        void *callback_data)
{
    (void)is_truncated;
    (void)next_marker;
    (void)common_prefixes_count;
    (void)common_prefixes;
    list_cb_data *data = (list_cb_data *)callback_data;
    for (int i = 0; i < contents_count && data->current_count < data->max_count; i++) {
        memset(&data->tasks[data->current_count], 0, sizeof(restore_task));
        strncpy(data->tasks[data->current_count].key, contents[i].key, MAX_KEY_LEN - 1);
        data->tasks[data->current_count].key[MAX_KEY_LEN - 1] = '\0';
        data->current_count++;
    }
    return OBS_STATUS_OK;
}
// 列举对象完成回调
static void list_complete_callback(obs_status status,
                                   const obs_error_details *error,
                                   void *callback_data)
{
    (void)error;
    list_cb_data *data = (list_cb_data *)callback_data;
    data->ret_status = status;
}
// 恢复状态轮询的数据结构
typedef struct {
    char restore_info[128];
    obs_status status;
} restore_status_data;
static obs_status restore_check_properties_callback(const obs_response_properties *properties, void *callback_data)
{
    restore_status_data *data = (restore_status_data *)callback_data;
    if (data && properties && properties->restore) {
        strncpy(data->restore_info, properties->restore, sizeof(data->restore_info) - 1);
        data->restore_info[sizeof(data->restore_info) - 1] = '\0';
    }
    return OBS_STATUS_OK;
}
static void restore_check_complete_callback(obs_status status,
                                            const obs_error_details *error,
                                            void *callback_data)
{
    (void)error;
    restore_status_data *data = (restore_status_data *)callback_data;
    if (data) {
        data->status = status;
    }
}
// 等待单个对象恢复完成（轮询对象元数据中的 restore 头）
static void wait_until_restore_complete(const obs_options *options, obs_object_info *object_info)
{
    // 恢复完成标记：restore 头以 ongoing-request="false" 开头
    const char *prefix = "ongoing-request=\"false\"";
    obs_response_handler handler = {
        restore_check_properties_callback,
        restore_check_complete_callback
    };
    for (;;) {
        restore_status_data data;
        memset(&data, 0, sizeof(data));
        data.status = OBS_STATUS_BUTT;
        get_object_metadata(options, object_info, NULL, &handler, &data);
        if (data.status != OBS_STATUS_OK) {
            printf("[WARN] get metadata for restore check failed(%s)\n", obs_get_status_name(data.status));
            break;
        }
        if (data.restore_info[0] != '\0') {
            // 恢复完成则退出
            if (strstr(data.restore_info, prefix) != NULL) {
                printf("[INFO] %s restore completed: %s\n", object_info->key, data.restore_info);
                break;
            }
        }
        // 未完成，等待后继续轮询
        sleep(RESTORE_WAIT_SECONDS);
    }
}
// 工作线程：领取任务 -> 恢复对象 -> 等待恢复完成
static void *worker_thread(void *arg)
{
    obs_options *options_ptr = (obs_options *)arg;
    const char *stored_days = "1";
    obs_tier tier = OBS_TIER_EXPEDITED;
    while (1) {
        pthread_mutex_lock(&g_task_mutex);
        if (g_task_index >= g_task_count) {
            pthread_mutex_unlock(&g_task_mutex);
            break;
        }
        int idx = g_task_index++;
        pthread_mutex_unlock(&g_task_mutex);
        restore_task *task = &g_tasks[idx];
        // 构造对象信息
        obs_object_info object_info;
        memset(&object_info, 0, sizeof(obs_object_info));
        object_info.key = task->key;
        object_info.version_id = NULL;
        // 设置响应回调
        obs_response_handler handler = {
            restore_properties_callback,
            restore_complete_callback
        };
        // 提交恢复请求
        task->status = OBS_STATUS_BUTT;
        restore_object(options_ptr, &object_info, stored_days, tier, &handler, task);
        if (task->status != OBS_STATUS_OK) {                                                                                                                                      
            // restore_complete_callback 已统计失败，跳过等待
            continue;
        }
        // 等待对象恢复完成
        wait_until_restore_complete(options_ptr, &object_info);
    }
    return NULL;
}
int main()
{
    // 本示例用于批量恢复归档或深度归档存储的对象。归档或深度归档类型的对象处于冷冻状态无法直接访问，
    // 需要先提交恢复请求，等待恢复完成后才能访问。
    // 在程序入口调用obs_initialize方法来初始化网络、内存等全局资源。
    const char *BUCKET_NAME = "example-bucket-name";
    const char *PREFIX = "test/source/";
    int THREAD_NUM = 8;
    obs_status status = obs_initialize(OBS_INIT_ALL);
    if (status != OBS_STATUS_OK) {
        printf("obs_initialize failed: %s\n", obs_get_status_name(status));
        return 1;
    }
    obs_options options;
    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");
    options.bucket_options.bucket_name = (char *)BUCKET_NAME;
    options.request_options.auth_switch = OBS_OBS_TYPE;
    printf("\nListing objects with prefix '%s'...\n", PREFIX);
    // 列举指定前缀的对象，收集待恢复对象列表
    g_tasks = (restore_task *)malloc(sizeof(restore_task) * MAX_TASK);
    if (!g_tasks) {
        printf("Failed to allocate memory\n");
        obs_deinitialize();
        return 1;
    }
    list_cb_data cb_data = {g_tasks, MAX_TASK, 0, OBS_STATUS_BUTT};
    obs_list_objects_handler list_handler = {
        {NULL, list_complete_callback},
        list_objects_callback
    };
    list_bucket_objects(&options, PREFIX, NULL, NULL, 1000, &list_handler, &cb_data);
    if (cb_data.ret_status != OBS_STATUS_OK) {
        printf("list_bucket_objects failed: %s\n", obs_get_status_name(cb_data.ret_status));
        free(g_tasks);
        obs_deinitialize();
        return 1;
    }
    g_task_count = cb_data.current_count;
    if (g_task_count == 0) {
        printf("No objects found with prefix '%s'\n", PREFIX);
        free(g_tasks);
        obs_deinitialize();
        return 0;
    }
    printf("Found %d objects to restore\n", g_task_count);
    g_result.total = g_task_count;
    g_result.success = 0;
    g_result.failed = 0;
    g_result.failed_count = 0;
    g_task_index = 0;
    // 多线程批量提交恢复请求并等待恢复完成
    pthread_t threads[THREAD_NUM];
    for (int i = 0; i < THREAD_NUM; i++) {
        pthread_create(&threads[i], NULL, worker_thread, &options);
    }
    for (int i = 0; i < THREAD_NUM; i++) {
        pthread_join(threads[i], NULL);
    }
    // 输出批量恢复统计
    printf("\n========== Batch Restore Summary ==========\n");
    printf("Total: %d\n", g_result.total);
    printf("Success: %d\n", g_result.success);
    printf("Failed: %d\n", g_result.failed);
    if (g_result.failed_count > 0) {
        printf("\nFailed objects:\n");
        for (int i = 0; i < g_result.failed_count; i++) {
            printf("  - %s\n", g_result.failed_keys[i]);
        }
    }
    printf("============================================\n");
    free(g_tasks);
    obs_deinitialize();
    return 0;
}
```
#### 相关链接
- 关于恢复归档存储对象的API说明，请参见[恢复归档或深度归档存储对象](https://support.huaweicloud.com/api-obs/obs_04_0087.html)。
- 更多关于恢复归档存储对象的代码示例，请参见[Github示例](https://github.com/huaweicloud/huaweicloud-sdk-c-obs/blob/master/source/eSDK_OBS_API/eSDK_OBS_API_C++_Demo/demo.c)。
- 恢复归档对象过程中返回的错误码含义、问题原因及处理措施可参考[OBS错误码](https://support.huaweicloud.com/api-obs/obs_04_0115.html#section1)。
 
