Sample code for 30+ languages & platforms
Unicode 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 Unicode C++ Downloads

Unicode C++
#include <CkHttpW.h>
#include <CkHttpResponseW.h>
#include <CkStringBuilderW.h>
#include <CkTaskW.h>
#include <CkFileAccessW.h>

void ChilkatSample(void)
    {
    bool success = false;

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

    CkHttpW http;

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

    CkHttpResponseW resp;
    success = http.HttpNoBody(L"HEAD",url,resp);
    if (success == false) {
        wprintf(L"%s\n",http.lastErrorText());
        return;
    }

    int remoteFileSize = resp.get_ContentLength();

    wprintf(L"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)
    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..
    CkHttpW http1;
    CkHttpW http2;
    CkHttpW http3;
    CkHttpW http4;

    CkStringBuilderW sbRange;
    sbRange.SetString(L"bytes=<range-start>-<range-end>");
    int numReplaced = sbRange.ReplaceI(L"<range-start>",0);
    numReplaced = sbRange.ReplaceI(L"<range-end>",chunkSize - 1);
    wprintf(L"%s\n",sbRange.getAsString());
    http1.SetRequestHeader(L"Range",sbRange.getAsString());

    sbRange.SetString(L"bytes=<range-start>-<range-end>");
    numReplaced = sbRange.ReplaceI(L"<range-start>",chunkSize);
    numReplaced = sbRange.ReplaceI(L"<range-end>",2 * chunkSize - 1);
    wprintf(L"%s\n",sbRange.getAsString());
    http2.SetRequestHeader(L"Range",sbRange.getAsString());

    sbRange.SetString(L"bytes=<range-start>-<range-end>");
    numReplaced = sbRange.ReplaceI(L"<range-start>",2 * chunkSize);
    numReplaced = sbRange.ReplaceI(L"<range-end>",3 * chunkSize - 1);
    wprintf(L"%s\n",sbRange.getAsString());
    http3.SetRequestHeader(L"Range",sbRange.getAsString());

    sbRange.SetString(L"bytes=<range-start>-");
    numReplaced = sbRange.ReplaceI(L"<range-start>",3 * chunkSize);
    wprintf(L"%s\n",sbRange.getAsString());
    http4.SetRequestHeader(L"Range",sbRange.getAsString());

    // Start each range download
    CkTaskW *task1 = http1.DownloadAsync(url,L"qa_output/chunk1.dat");
    task1->Run();

    CkTaskW *task2 = http2.DownloadAsync(url,L"qa_output/chunk2.dat");
    task2->Run();

    CkTaskW *task3 = http3.DownloadAsync(url,L"qa_output/chunk3.dat");
    task3->Run();

    CkTaskW *task4 = http4.DownloadAsync(url,L"qa_output/chunk4.dat");
    task4->Run();

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

        if (task2->get_Live() == true) {
            numLive = numLive + 1;
        }

        if (task3->get_Live() == true) {
            numLive = numLive + 1;
        }

        if (task4->get_Live() == 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..
            task1->SleepMs(10);
        }

    }

    // All should be downloaded now..
    // Examine the result of each Download.
    int numErrors = 0;
    if (task1->GetResultBool() == false) {
        wprintf(L"%s\n",task1->resultErrorText());
        numErrors = numErrors + 1;
    }

    if (task2->GetResultBool() == false) {
        wprintf(L"%s\n",task2->resultErrorText());
        numErrors = numErrors + 1;
    }

    if (task3->GetResultBool() == false) {
        wprintf(L"%s\n",task3->resultErrorText());
        numErrors = numErrors + 1;
    }

    if (task4->GetResultBool() == false) {
        wprintf(L"%s\n",task4->resultErrorText());
        numErrors = numErrors + 1;
    }

    if (numErrors > 0) {
        delete task1;
        delete task2;
        delete task3;
        delete task4;
        return;
    }

    // All downloads were successful.
    // Compose the file from the parts.
    CkFileAccessW fac;
    success = fac.ReassembleFile(L"qa_output",L"chunk",L"dat",L"qa_output/hamlet.xml");
    if (success == false) {
        wprintf(L"%s\n",fac.lastErrorText());
    }
    else {
        wprintf(L"Success.\n");
    }

    delete task1;
    delete task2;
    delete task3;
    delete task4;

    // Let's download in the regular way, and then compare files..
    success = http.Download(url,L"qa_output/hamletRegular.xml");

    // Compare files.
    bool bSame = fac.FileContentsEqual(L"qa_output/hamlet.xml",L"qa_output/hamletRegular.xml");
    wprintf(L"bSame = %d\n",bSame);
    }