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

C++
#include <CkHttp.h>
#include <CkHttpResponse.h>
#include <CkStringBuilder.h>
#include <CkTask.h>
#include <CkFileAccess.h>

void ChilkatSample(void)
    {
    bool success = false;

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

    CkHttp http;

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

    CkHttpResponse resp;
    success = http.HttpNoBody("HEAD",url,resp);
    if (success == false) {
        std::cout << http.lastErrorText() << "\r\n";
        return;
    }

    int remoteFileSize = resp.get_ContentLength();

    std::cout << "Downloading " << remoteFileSize << " bytes..." << "\r\n";

    // 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..
    CkHttp http1;
    CkHttp http2;
    CkHttp http3;
    CkHttp http4;

    CkStringBuilder sbRange;
    sbRange.SetString("bytes=<range-start>-<range-end>");
    int numReplaced = sbRange.ReplaceI("<range-start>",0);
    numReplaced = sbRange.ReplaceI("<range-end>",chunkSize - 1);
    std::cout << sbRange.getAsString() << "\r\n";
    http1.SetRequestHeader("Range",sbRange.getAsString());

    sbRange.SetString("bytes=<range-start>-<range-end>");
    numReplaced = sbRange.ReplaceI("<range-start>",chunkSize);
    numReplaced = sbRange.ReplaceI("<range-end>",2 * chunkSize - 1);
    std::cout << sbRange.getAsString() << "\r\n";
    http2.SetRequestHeader("Range",sbRange.getAsString());

    sbRange.SetString("bytes=<range-start>-<range-end>");
    numReplaced = sbRange.ReplaceI("<range-start>",2 * chunkSize);
    numReplaced = sbRange.ReplaceI("<range-end>",3 * chunkSize - 1);
    std::cout << sbRange.getAsString() << "\r\n";
    http3.SetRequestHeader("Range",sbRange.getAsString());

    sbRange.SetString("bytes=<range-start>-");
    numReplaced = sbRange.ReplaceI("<range-start>",3 * chunkSize);
    std::cout << sbRange.getAsString() << "\r\n";
    http4.SetRequestHeader("Range",sbRange.getAsString());

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

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

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

    CkTask *task4 = http4.DownloadAsync(url,"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) {
        std::cout << task1->resultErrorText() << "\r\n";
        numErrors = numErrors + 1;
    }

    if (task2->GetResultBool() == false) {
        std::cout << task2->resultErrorText() << "\r\n";
        numErrors = numErrors + 1;
    }

    if (task3->GetResultBool() == false) {
        std::cout << task3->resultErrorText() << "\r\n";
        numErrors = numErrors + 1;
    }

    if (task4->GetResultBool() == false) {
        std::cout << task4->resultErrorText() << "\r\n";
        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.
    CkFileAccess fac;
    success = fac.ReassembleFile("qa_output","chunk","dat","qa_output/hamlet.xml");
    if (success == false) {
        std::cout << fac.lastErrorText() << "\r\n";
    }
    else {
        std::cout << "Success." << "\r\n";
    }

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

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

    // Compare files.
    bool bSame = fac.FileContentsEqual("qa_output/hamlet.xml","qa_output/hamletRegular.xml");
    std::cout << "bSame = " << bSame << "\r\n";
    }