Sample code for 30+ languages & platforms
PureBasic

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 PureBasic Downloads

PureBasic
IncludeFile "CkHttpResponse.pb"
IncludeFile "CkHttp.pb"
IncludeFile "CkStringBuilder.pb"
IncludeFile "CkTask.pb"
IncludeFile "CkFileAccess.pb"

Procedure ChilkatExample()

    success.i = 0

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

    http.i = CkHttp::ckCreate()
    If http.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

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

    resp.i = CkHttpResponse::ckCreate()
    If resp.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkHttp::ckHttpNoBody(http,"HEAD",url,resp)
    If success = 0
        Debug CkHttp::ckLastErrorText(http)
        CkHttp::ckDispose(http)
        CkHttpResponse::ckDispose(resp)
        ProcedureReturn
    EndIf

    remoteFileSize.i = CkHttpResponse::ckContentLength(resp)

    Debug "Downloading " + Str(remoteFileSize) + " bytes..."

    ; Let's download in 4 chunks.
    ; (the last chunk will be whatever remains after the 1st 3 equal sized chunks)
    chunkSize.i = 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.i = CkHttp::ckCreate()
    If http1.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    http2.i = CkHttp::ckCreate()
    If http2.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    http3.i = CkHttp::ckCreate()
    If http3.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    http4.i = CkHttp::ckCreate()
    If http4.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    sbRange.i = CkStringBuilder::ckCreate()
    If sbRange.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    CkStringBuilder::ckSetString(sbRange,"bytes=<range-start>-<range-end>")
    numReplaced.i = CkStringBuilder::ckReplaceI(sbRange,"<range-start>",0)
    numReplaced = CkStringBuilder::ckReplaceI(sbRange,"<range-end>",chunkSize - 1)
    Debug CkStringBuilder::ckGetAsString(sbRange)
    CkHttp::ckSetRequestHeader(http1,"Range",CkStringBuilder::ckGetAsString(sbRange))

    CkStringBuilder::ckSetString(sbRange,"bytes=<range-start>-<range-end>")
    numReplaced = CkStringBuilder::ckReplaceI(sbRange,"<range-start>",chunkSize)
    numReplaced = CkStringBuilder::ckReplaceI(sbRange,"<range-end>",2 * chunkSize - 1)
    Debug CkStringBuilder::ckGetAsString(sbRange)
    CkHttp::ckSetRequestHeader(http2,"Range",CkStringBuilder::ckGetAsString(sbRange))

    CkStringBuilder::ckSetString(sbRange,"bytes=<range-start>-<range-end>")
    numReplaced = CkStringBuilder::ckReplaceI(sbRange,"<range-start>",2 * chunkSize)
    numReplaced = CkStringBuilder::ckReplaceI(sbRange,"<range-end>",3 * chunkSize - 1)
    Debug CkStringBuilder::ckGetAsString(sbRange)
    CkHttp::ckSetRequestHeader(http3,"Range",CkStringBuilder::ckGetAsString(sbRange))

    CkStringBuilder::ckSetString(sbRange,"bytes=<range-start>-")
    numReplaced = CkStringBuilder::ckReplaceI(sbRange,"<range-start>",3 * chunkSize)
    Debug CkStringBuilder::ckGetAsString(sbRange)
    CkHttp::ckSetRequestHeader(http4,"Range",CkStringBuilder::ckGetAsString(sbRange))

    ; Start each range download
    task1.i = CkHttp::ckDownloadAsync(http1,url,"qa_output/chunk1.dat")
    CkTask::ckRun(task1)

    task2.i = CkHttp::ckDownloadAsync(http2,url,"qa_output/chunk2.dat")
    CkTask::ckRun(task2)

    task3.i = CkHttp::ckDownloadAsync(http3,url,"qa_output/chunk3.dat")
    CkTask::ckRun(task3)

    task4.i = CkHttp::ckDownloadAsync(http4,url,"qa_output/chunk4.dat")
    CkTask::ckRun(task4)

    ; Wait for the downloads to complete.
    numLive.i = 4
    While numLive > 0
        numLive = 0
        If CkTask::ckLive(task1) = 1
            numLive = numLive + 1
        EndIf

        If CkTask::ckLive(task2) = 1
            numLive = numLive + 1
        EndIf

        If CkTask::ckLive(task3) = 1
            numLive = numLive + 1
        EndIf

        If CkTask::ckLive(task4) = 1
            numLive = numLive + 1
        EndIf

        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::ckSleepMs(task1,10)
        EndIf

    Wend

    ; All should be downloaded now..
    ; Examine the result of each Download.
    numErrors.i = 0
    If CkTask::ckGetResultBool(task1) = 0
        Debug CkTask::ckResultErrorText(task1)
        numErrors = numErrors + 1
    EndIf

    If CkTask::ckGetResultBool(task2) = 0
        Debug CkTask::ckResultErrorText(task2)
        numErrors = numErrors + 1
    EndIf

    If CkTask::ckGetResultBool(task3) = 0
        Debug CkTask::ckResultErrorText(task3)
        numErrors = numErrors + 1
    EndIf

    If CkTask::ckGetResultBool(task4) = 0
        Debug CkTask::ckResultErrorText(task4)
        numErrors = numErrors + 1
    EndIf

    If numErrors > 0
        CkTask::ckDispose(task1)

        CkTask::ckDispose(task2)

        CkTask::ckDispose(task3)

        CkTask::ckDispose(task4)

        CkHttp::ckDispose(http)
        CkHttpResponse::ckDispose(resp)
        CkHttp::ckDispose(http1)
        CkHttp::ckDispose(http2)
        CkHttp::ckDispose(http3)
        CkHttp::ckDispose(http4)
        CkStringBuilder::ckDispose(sbRange)
        ProcedureReturn
    EndIf

    ; All downloads were successful.
    ; Compose the file from the parts.
    fac.i = CkFileAccess::ckCreate()
    If fac.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkFileAccess::ckReassembleFile(fac,"qa_output","chunk","dat","qa_output/hamlet.xml")
    If success = 0
        Debug CkFileAccess::ckLastErrorText(fac)
    Else
        Debug "Success."
    EndIf

    CkTask::ckDispose(task1)

    CkTask::ckDispose(task2)

    CkTask::ckDispose(task3)

    CkTask::ckDispose(task4)

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

    ; Compare files.
    bSame.i = CkFileAccess::ckFileContentsEqual(fac,"qa_output/hamlet.xml","qa_output/hamletRegular.xml")
    Debug "bSame = " + Str(bSame)


    CkHttp::ckDispose(http)
    CkHttpResponse::ckDispose(resp)
    CkHttp::ckDispose(http1)
    CkHttp::ckDispose(http2)
    CkHttp::ckDispose(http3)
    CkHttp::ckDispose(http4)
    CkStringBuilder::ckDispose(sbRange)
    CkFileAccess::ckDispose(fac)


    ProcedureReturn
EndProcedure