Objective-C
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
#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);