Updated on 2026-09-22 GMT+08:00

Restoring Archive Objects in Batches

This example restores Archive objects in batches. You can specify the list of object keys to be restored, restoration speed (Expedited or Standard), and retention period, and initiate restoration requests concurrently. This method is suitable for batch restoration of a large amount of archived data.

#import <Foundation/Foundation.h>
#import <OBS/OBS.h>
#pragma mark - Result Model
@interface BatchRestoreResult : NSObject
@property (nonatomic, copy) NSString *objectKey;
@property (nonatomic, assign) BOOL success;
@property (nonatomic, strong) NSError *error;
@property (nonatomic, copy) NSString *httpStatusCode;
@end
@implementation BatchRestoreResult
@end
#pragma mark - Batch Restorer
@interface BatchArchiveRestorer : NSObject
@property (nonatomic, strong) OBSClient *client;
@property (nonatomic, copy) NSString *bucketName;
@property (nonatomic, assign) NSInteger concurrentCount;
@property (nonatomic, assign) OBSRestoreTier restoreTier;
@property (nonatomic, assign) NSInteger storeDays;
@property (nonatomic, strong) NSMutableArray<BatchRestoreResult *> *results;
@property (nonatomic, strong) dispatch_queue_t resultQueue;
- (instancetype)initWithClient:(OBSClient *)client
                    bucketName:(NSString *)bucketName
               concurrentCount:(NSInteger)concurrentCount
                    restoreTier:(OBSRestoreTier)restoreTier
                     storeDays:(NSInteger)storeDays;
- (void)restoreObjects:(NSArray<NSString *> *)objectKeys
          waitUntilDone:(BOOL)wait;
@end
@implementation BatchArchiveRestorer
- (instancetype)initWithClient:(OBSClient *)client
                    bucketName:(NSString *)bucketName
               concurrentCount:(NSInteger)concurrentCount
                    restoreTier:(OBSRestoreTier)restoreTier
                     storeDays:(NSInteger)storeDays {
    self = [super init];
    if (self) {
        _client = client;
        _bucketName = bucketName;
        _concurrentCount = MIN(concurrentCount > 0 ? concurrentCount : 3, 10);
        _restoreTier = restoreTier;
        _storeDays = (storeDays > 0 && storeDays <= 30) ? storeDays : 1;
        _results = [NSMutableArray array];
        _resultQueue = dispatch_queue_create("com.obs.batchrestore.result", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}
- (void)restoreObjects:(NSArray<NSString *> *)objectKeys
          waitUntilDone:(BOOL)wait {
    dispatch_group_t group = dispatch_group_create();
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(self.concurrentCount);
    dispatch_queue_t queue = dispatch_queue_create("com.obs.batchrestore", DISPATCH_QUEUE_CONCURRENT);
    NSString *tierStr = (self.restoreTier == OBSRestoreTierExpedited) ? @"Expedited" : @"Standard";
    NSLog(@"[BatchRestore] Starting %lu restores, concurrency: %ld, tier: %@, days: %ld",
          (unsigned long)objectKeys.count, (long)self.concurrentCount, tierStr, (long)self.storeDays);
    for (NSString *objectKey in objectKeys) {
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 300 * NSEC_PER_SEC));
            [self restoreObject:objectKey completion:^{
                dispatch_semaphore_signal(semaphore);
                dispatch_group_leave(group);
            }];
        });
    }
    if (wait) {
        dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 300 * NSEC_PER_SEC));
        [self printSummary];
    }
}
- (void)restoreObject:(NSString *)objectKey completion:(void (^)(void))completion {
    OBSRestoreObjectRequest *request = [[OBSRestoreObjectRequest alloc] initWithBucketName:self.bucketName
                                                                                objectKey:objectKey
                                                                                storeDays:@(self.storeDays)];
    request.restoreTier = self.restoreTier;
    __weak typeof(self) weakSelf = self;    __strong typeof(weakSelf) strongSelf = weakSelf;    if (!strongSelf) return;    [self.client restoreObject:request completionHandler:^(OBSRestoreObjectResponse *response, NSError *error) {
        BatchRestoreResult *result = [[BatchRestoreResult alloc] init];
        result.objectKey = objectKey;
        result.error = error;
        result.success = (error == nil);
        result.httpStatusCode = response.statusCode ?: @"-";
        if (error) {
            NSLog(@"[Restore] FAILED - %@ : %@", objectKey, error.localizedDescription);
        } else {
            NSLog(@"[Restore] SUCCESS - %@ (HTTP %@)", objectKey, result.httpStatusCode);
        }
        dispatch_sync(strongSelf.resultQueue, ^{ [strongSelf.results addObject:result]; });
        if (completion) completion();
    }];
}
- (void)printSummary {
    NSInteger success = 0, failure = 0;
    for (BatchRestoreResult *r in self.results) {
        if (r.success) success++;
        else failure++;
    }
    NSLog(@"\n========== Batch Restore Summary ==========");
    NSLog(@"Total: %lu | Success: %ld | Failed: %ld", (unsigned long)self.results.count, (long)success, (long)failure);
    if (failure > 0) {
        NSLog(@"Failed items:");
        for (BatchRestoreResult *r in self.results) {
            if (!r.success) {
                NSLog(@"  - %@ : %@", r.objectKey, r.error.localizedDescription);
            }
        }
    }
    NSLog(@"============================================\n");
}
@end
#pragma mark - Entry Point
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // === Configuration ===
        NSString *endPoint = @"https://your-endpoint";
        // Hard-coded or plaintext AK and SK are risky. For security purposes, encrypt your AK and SK before storing them in the configuration file or environment variables. In this example, the AK and SK are stored in environment variables. Before running the code in this example, configure environment variables AccessKeyID and SecretAccessKey.
        // Obtain an AK/SK pair on the management console. For details, see https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html.
        char* ak_env = getenv("AccessKeyID");
        char* sk_env = getenv("SecretAccessKey");
        if (ak_env == NULL || sk_env == NULL) {
            NSLog(@"Error: AccessKeyID or SecretAccessKey environment variable not set");
            return 1;
        }
        NSString *AK = [NSString stringWithUTF8String:ak_env];
        NSString *SK = [NSString stringWithUTF8String:sk_env];
        NSString *bucketName = @"your-bucket-name";
        NSInteger concurrentCount = 3;  // Adjust as needed
        // Restore options
        OBSRestoreTier restoreTier = OBSRestoreTierStandard;  // or OBSRestoreTierExpedited
        NSInteger storeDays = 1;                              // 1-30 days
        // Objects to restore (must be in Archive or Cold storage class)
        NSArray<NSString *> *objectKeys = @[
            @"archive_folder/doc1.pdf",
            @"archive_folder/doc2.jpg",
            @"archive_folder/doc3.zip",
            @"archive_folder/doc4.mp4",
            @"archive_folder/doc5.tar"
        ];
        // === Initialize OBS Client ===
        OBSStaticCredentialProvider *credentialProvider = [[OBSStaticCredentialProvider alloc] initWithAccessKey:AK secretKey:SK];
        OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURLString:endPoint credentialProvider:credentialProvider];
        OBSClient *client = [[OBSClient alloc] initWithConfiguration:conf];
        // === Batch Restore ===
        BatchArchiveRestorer *restorer = [[BatchArchiveRestorer alloc] initWithClient:client
                                                                          bucketName:bucketName
                                                                     concurrentCount:concurrentCount
                                                                          restoreTier:restoreTier
                                                                           storeDays:storeDays];
        [restorer restoreObjects:objectKeys waitUntilDone:YES];
    }
    return 0;
}