C
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 C Downloads
#include <C_CkHttp.h>
#include <C_CkHttpResponse.h>
#include <C_CkStringBuilder.h>
#include <C_CkTask.h>
#include <C_CkFileAccess.h>
void ChilkatSample(void)
{
BOOL success;
HCkHttp http;
const char *url;
HCkHttpResponse resp;
int remoteFileSize;
int chunkSize;
HCkHttp http1;
HCkHttp http2;
HCkHttp http3;
HCkHttp http4;
HCkStringBuilder sbRange;
int numReplaced;
HCkTask task1;
HCkTask task2;
HCkTask task3;
HCkTask task4;
int numLive;
int numErrors;
HCkFileAccess fac;
BOOL bSame;
success = FALSE;
// This requires the Chilkat API to have been previously unlocked.
// See Global Unlock Sample for sample code.
http = CkHttp_Create();
// First get the size of the file to be downloaded.
url = "https://www.chilkatsoft.com/hamlet.xml";
resp = CkHttpResponse_Create();
success = CkHttp_HttpNoBody(http,"HEAD",url,resp);
if (success == FALSE) {
printf("%s\n",CkHttp_lastErrorText(http));
CkHttp_Dispose(http);
CkHttpResponse_Dispose(resp);
return;
}
remoteFileSize = CkHttpResponse_getContentLength(resp);
printf("Downloading %d bytes...\n",remoteFileSize);
// Let's download in 4 chunks.
// (the last chunk will be whatever remains after the 1st 3 equal sized chunks)
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..
http1 = CkHttp_Create();
http2 = CkHttp_Create();
http3 = CkHttp_Create();
http4 = CkHttp_Create();
sbRange = CkStringBuilder_Create();
CkStringBuilder_SetString(sbRange,"bytes=<range-start>-<range-end>");
numReplaced = CkStringBuilder_ReplaceI(sbRange,"<range-start>",0);
numReplaced = CkStringBuilder_ReplaceI(sbRange,"<range-end>",chunkSize - 1);
printf("%s\n",CkStringBuilder_getAsString(sbRange));
CkHttp_SetRequestHeader(http1,"Range",CkStringBuilder_getAsString(sbRange));
CkStringBuilder_SetString(sbRange,"bytes=<range-start>-<range-end>");
numReplaced = CkStringBuilder_ReplaceI(sbRange,"<range-start>",chunkSize);
numReplaced = CkStringBuilder_ReplaceI(sbRange,"<range-end>",2 * chunkSize - 1);
printf("%s\n",CkStringBuilder_getAsString(sbRange));
CkHttp_SetRequestHeader(http2,"Range",CkStringBuilder_getAsString(sbRange));
CkStringBuilder_SetString(sbRange,"bytes=<range-start>-<range-end>");
numReplaced = CkStringBuilder_ReplaceI(sbRange,"<range-start>",2 * chunkSize);
numReplaced = CkStringBuilder_ReplaceI(sbRange,"<range-end>",3 * chunkSize - 1);
printf("%s\n",CkStringBuilder_getAsString(sbRange));
CkHttp_SetRequestHeader(http3,"Range",CkStringBuilder_getAsString(sbRange));
CkStringBuilder_SetString(sbRange,"bytes=<range-start>-");
numReplaced = CkStringBuilder_ReplaceI(sbRange,"<range-start>",3 * chunkSize);
printf("%s\n",CkStringBuilder_getAsString(sbRange));
CkHttp_SetRequestHeader(http4,"Range",CkStringBuilder_getAsString(sbRange));
// Start each range download
task1 = CkHttp_DownloadAsync(http1,url,"qa_output/chunk1.dat");
CkTask_Run(task1);
task2 = CkHttp_DownloadAsync(http2,url,"qa_output/chunk2.dat");
CkTask_Run(task2);
task3 = CkHttp_DownloadAsync(http3,url,"qa_output/chunk3.dat");
CkTask_Run(task3);
task4 = CkHttp_DownloadAsync(http4,url,"qa_output/chunk4.dat");
CkTask_Run(task4);
// Wait for the downloads to complete.
numLive = 4;
while (numLive > 0) {
numLive = 0;
if (CkTask_getLive(task1) == TRUE) {
numLive = numLive + 1;
}
if (CkTask_getLive(task2) == TRUE) {
numLive = numLive + 1;
}
if (CkTask_getLive(task3) == TRUE) {
numLive = numLive + 1;
}
if (CkTask_getLive(task4) == TRUE) {
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..
CkTask_SleepMs(task1,10);
}
}
// All should be downloaded now..
// Examine the result of each Download.
numErrors = 0;
if (CkTask_GetResultBool(task1) == FALSE) {
printf("%s\n",CkTask_resultErrorText(task1));
numErrors = numErrors + 1;
}
if (CkTask_GetResultBool(task2) == FALSE) {
printf("%s\n",CkTask_resultErrorText(task2));
numErrors = numErrors + 1;
}
if (CkTask_GetResultBool(task3) == FALSE) {
printf("%s\n",CkTask_resultErrorText(task3));
numErrors = numErrors + 1;
}
if (CkTask_GetResultBool(task4) == FALSE) {
printf("%s\n",CkTask_resultErrorText(task4));
numErrors = numErrors + 1;
}
if (numErrors > 0) {
CkTask_Dispose(task1);
CkTask_Dispose(task2);
CkTask_Dispose(task3);
CkTask_Dispose(task4);
CkHttp_Dispose(http);
CkHttpResponse_Dispose(resp);
CkHttp_Dispose(http1);
CkHttp_Dispose(http2);
CkHttp_Dispose(http3);
CkHttp_Dispose(http4);
CkStringBuilder_Dispose(sbRange);
return;
}
// All downloads were successful.
// Compose the file from the parts.
fac = CkFileAccess_Create();
success = CkFileAccess_ReassembleFile(fac,"qa_output","chunk","dat","qa_output/hamlet.xml");
if (success == FALSE) {
printf("%s\n",CkFileAccess_lastErrorText(fac));
}
else {
printf("Success.\n");
}
CkTask_Dispose(task1);
CkTask_Dispose(task2);
CkTask_Dispose(task3);
CkTask_Dispose(task4);
// Let's download in the regular way, and then compare files..
success = CkHttp_Download(http,url,"qa_output/hamletRegular.xml");
// Compare files.
bSame = CkFileAccess_FileContentsEqual(fac,"qa_output/hamlet.xml","qa_output/hamletRegular.xml");
printf("bSame = %d\n",bSame);
CkHttp_Dispose(http);
CkHttpResponse_Dispose(resp);
CkHttp_Dispose(http1);
CkHttp_Dispose(http2);
CkHttp_Dispose(http3);
CkHttp_Dispose(http4);
CkStringBuilder_Dispose(sbRange);
CkFileAccess_Dispose(fac);
}