Sample code for 30+ languages & platforms
Objective-C

HTTP Download in Parallel with Simultaneous Range Requests

See more HTTP Examples

Demonstrates how to download a large file with parallel simultaneous requests, where each request downloads a segment (range) of the remote file.

Chilkat Objective-C Downloads

Objective-C
#import <CkoHttp.h>
#import <NSString.h>
#import <CkoHttpResponse.h>
#import <CkoStringBuilder.h>
#import <CkoTask.h>
#import <CkoFileAccess.h>

BOOL success = NO;

// This requires the Chilkat API to have been previously unlocked.
// See Global Unlock Sample for sample code.

CkoHttp *http = [[CkoHttp alloc] init];

// First get the size of the file to be downloaded.
NSString *url = @"https://www.chilkatsoft.com/hamlet.xml";

CkoHttpResponse *resp = [[CkoHttpResponse alloc] init];
success = [http HttpNoBody: @"HEAD" url: url response: resp];
if (success == NO) {
    NSLog(@"%@",http.LastErrorText);
    return;
}

int remoteFileSize = resp.ContentLength;

NSLog(@"%@%d%@",@"Downloading ",remoteFileSize,@" bytes...");

// Let's download in 4 chunks.
// (the last chunk will be whatever remains after the 1st 3 equal sized chunks)
int chunkSize = remoteFileSize / 4;

// The Range header is used to download a range from a resource
// Range: bytes=<range-start>-<range-end>
// or
// Range: bytes=<range-start>-

// We're writing code this way for clarity..
CkoHttp *http1 = [[CkoHttp alloc] init];
CkoHttp *http2 = [[CkoHttp alloc] init];
CkoHttp *http3 = [[CkoHttp alloc] init];
CkoHttp *http4 = [[CkoHttp alloc] init];

CkoStringBuilder *sbRange = [[CkoStringBuilder alloc] init];
[sbRange SetString: @"bytes=<range-start>-<range-end>"];
int numReplaced = [[sbRange ReplaceI: @"<range-start>" replacement: [NSNumber numberWithInt: 0]] intValue];
numReplaced = [[sbRange ReplaceI: @"<range-end>" replacement: [NSNumber numberWithInt: (chunkSize - 1)]] intValue];
NSLog(@"%@",[sbRange GetAsString]);
[http1 SetRequestHeader: @"Range" value: [sbRange GetAsString]];

[sbRange SetString: @"bytes=<range-start>-<range-end>"];
numReplaced = [[sbRange ReplaceI: @"<range-start>" replacement: [NSNumber numberWithInt: chunkSize]] intValue];
numReplaced = [[sbRange ReplaceI: @"<range-end>" replacement: [NSNumber numberWithInt: (2 * chunkSize - 1)]] intValue];
NSLog(@"%@",[sbRange GetAsString]);
[http2 SetRequestHeader: @"Range" value: [sbRange GetAsString]];

[sbRange SetString: @"bytes=<range-start>-<range-end>"];
numReplaced = [[sbRange ReplaceI: @"<range-start>" replacement: [NSNumber numberWithInt: (2 * chunkSize)]] intValue];
numReplaced = [[sbRange ReplaceI: @"<range-end>" replacement: [NSNumber numberWithInt: (3 * chunkSize - 1)]] intValue];
NSLog(@"%@",[sbRange GetAsString]);
[http3 SetRequestHeader: @"Range" value: [sbRange GetAsString]];

[sbRange SetString: @"bytes=<range-start>-"];
numReplaced = [[sbRange ReplaceI: @"<range-start>" replacement: [NSNumber numberWithInt: (3 * chunkSize)]] intValue];
NSLog(@"%@",[sbRange GetAsString]);
[http4 SetRequestHeader: @"Range" value: [sbRange GetAsString]];

// Start each range download
CkoTask *task1 = [http1 DownloadAsync: url saveToPath: @"qa_output/chunk1.dat"];
[task1 Run];

CkoTask *task2 = [http2 DownloadAsync: url saveToPath: @"qa_output/chunk2.dat"];
[task2 Run];

CkoTask *task3 = [http3 DownloadAsync: url saveToPath: @"qa_output/chunk3.dat"];
[task3 Run];

CkoTask *task4 = [http4 DownloadAsync: url saveToPath: @"qa_output/chunk4.dat"];
[task4 Run];

// Wait for the downloads to complete.
int numLive = 4;
while (numLive > 0) {
    numLive = 0;
    if (task1.Live == YES) {
        numLive = numLive + 1;
    }

    if (task2.Live == YES) {
        numLive = numLive + 1;
    }

    if (task3.Live == YES) {
        numLive = numLive + 1;
    }

    if (task4.Live == YES) {
        numLive = numLive + 1;
    }

    if (numLive > 0) {
        // SleepMs is a convenience method to cause the caller to sleep for N millisec.
        // It does not cause the given task to sleep..
        [task1 SleepMs: [NSNumber numberWithInt: 10]];
    }

}

// All should be downloaded now..
// Examine the result of each Download.
int numErrors = 0;
if ([task1 GetResultBool] == NO) {
    NSLog(@"%@",task1.ResultErrorText);
    numErrors = numErrors + 1;
}

if ([task2 GetResultBool] == NO) {
    NSLog(@"%@",task2.ResultErrorText);
    numErrors = numErrors + 1;
}

if ([task3 GetResultBool] == NO) {
    NSLog(@"%@",task3.ResultErrorText);
    numErrors = numErrors + 1;
}

if ([task4 GetResultBool] == NO) {
    NSLog(@"%@",task4.ResultErrorText);
    numErrors = numErrors + 1;
}

if (numErrors > 0) {

    return;
}

// All downloads were successful.
// Compose the file from the parts.
CkoFileAccess *fac = [[CkoFileAccess alloc] init];
success = [fac ReassembleFile: @"qa_output" partPrefix: @"chunk" partExtension: @"dat" reassembledFilename: @"qa_output/hamlet.xml"];
if (success == NO) {
    NSLog(@"%@",fac.LastErrorText);
}
else {
    NSLog(@"%@",@"Success.");
}

// Let's download in the regular way, and then compare files..
success = [http Download: url saveToPath: @"qa_output/hamletRegular.xml"];

// Compare files.
BOOL bSame = [fac FileContentsEqual: @"qa_output/hamlet.xml" path2: @"qa_output/hamletRegular.xml"];
NSLog(@"%@%d",@"bSame = ",bSame);