Sample code for 30+ languages & platforms
Tcl

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

Tcl

load ./chilkat.dll

set success 0

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

set http [new_CkHttp]

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

set resp [new_CkHttpResponse]

set success [CkHttp_HttpNoBody $http "HEAD" $url $resp]
if {$success == 0} then {
    puts [CkHttp_lastErrorText $http]
    delete_CkHttp $http
    delete_CkHttpResponse $resp
    exit
}

set remoteFileSize [CkHttpResponse_get_ContentLength $resp]

puts "Downloading $remoteFileSize bytes..."

# Let's download in 4 chunks.
# (the last chunk will be whatever remains after the 1st 3 equal sized chunks)
set chunkSize [expr $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..
set http1 [new_CkHttp]

set http2 [new_CkHttp]

set http3 [new_CkHttp]

set http4 [new_CkHttp]

set sbRange [new_CkStringBuilder]

CkStringBuilder_SetString $sbRange "bytes=<range-start>-<range-end>"
set numReplaced [CkStringBuilder_ReplaceI $sbRange "<range-start>" 0]
set numReplaced [CkStringBuilder_ReplaceI $sbRange "<range-end>" [expr $chunkSize - 1]]
puts [CkStringBuilder_getAsString $sbRange]
CkHttp_SetRequestHeader $http1 "Range" [CkStringBuilder_getAsString $sbRange]

CkStringBuilder_SetString $sbRange "bytes=<range-start>-<range-end>"
set numReplaced [CkStringBuilder_ReplaceI $sbRange "<range-start>" $chunkSize]
set numReplaced [CkStringBuilder_ReplaceI $sbRange "<range-end>" [expr [expr 2 * $chunkSize] - 1]]
puts [CkStringBuilder_getAsString $sbRange]
CkHttp_SetRequestHeader $http2 "Range" [CkStringBuilder_getAsString $sbRange]

CkStringBuilder_SetString $sbRange "bytes=<range-start>-<range-end>"
set numReplaced [CkStringBuilder_ReplaceI $sbRange "<range-start>" [expr 2 * $chunkSize]]
set numReplaced [CkStringBuilder_ReplaceI $sbRange "<range-end>" [expr [expr 3 * $chunkSize] - 1]]
puts [CkStringBuilder_getAsString $sbRange]
CkHttp_SetRequestHeader $http3 "Range" [CkStringBuilder_getAsString $sbRange]

CkStringBuilder_SetString $sbRange "bytes=<range-start>-"
set numReplaced [CkStringBuilder_ReplaceI $sbRange "<range-start>" [expr 3 * $chunkSize]]
puts [CkStringBuilder_getAsString $sbRange]
CkHttp_SetRequestHeader $http4 "Range" [CkStringBuilder_getAsString $sbRange]

# Start each range download
# task1 is a CkTask
set task1 [CkHttp_DownloadAsync $http1 $url "qa_output/chunk1.dat"]
CkTask_Run $task1

# task2 is a CkTask
set task2 [CkHttp_DownloadAsync $http2 $url "qa_output/chunk2.dat"]
CkTask_Run $task2

# task3 is a CkTask
set task3 [CkHttp_DownloadAsync $http3 $url "qa_output/chunk3.dat"]
CkTask_Run $task3

# task4 is a CkTask
set task4 [CkHttp_DownloadAsync $http4 $url "qa_output/chunk4.dat"]
CkTask_Run $task4

# Wait for the downloads to complete.
set numLive 4
while {$numLive > 0} {
    set numLive 0
    if {[CkTask_get_Live $task1] == 1} then {
        set numLive [expr $numLive + 1]
    }

    if {[CkTask_get_Live $task2] == 1} then {
        set numLive [expr $numLive + 1]
    }

    if {[CkTask_get_Live $task3] == 1} then {
        set numLive [expr $numLive + 1]
    }

    if {[CkTask_get_Live $task4] == 1} then {
        set numLive [expr $numLive + 1]
    }

    if {$numLive > 0} then {
        # 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.
set numErrors 0
if {[CkTask_GetResultBool $task1] == 0} then {
    puts [CkTask_resultErrorText $task1]
    set numErrors [expr $numErrors + 1]
}

if {[CkTask_GetResultBool $task2] == 0} then {
    puts [CkTask_resultErrorText $task2]
    set numErrors [expr $numErrors + 1]
}

if {[CkTask_GetResultBool $task3] == 0} then {
    puts [CkTask_resultErrorText $task3]
    set numErrors [expr $numErrors + 1]
}

if {[CkTask_GetResultBool $task4] == 0} then {
    puts [CkTask_resultErrorText $task4]
    set numErrors [expr $numErrors + 1]
}

if {$numErrors > 0} then {
    delete_CkTask $task1

    delete_CkTask $task2

    delete_CkTask $task3

    delete_CkTask $task4

    delete_CkHttp $http
    delete_CkHttpResponse $resp
    delete_CkHttp $http1
    delete_CkHttp $http2
    delete_CkHttp $http3
    delete_CkHttp $http4
    delete_CkStringBuilder $sbRange
    exit
}

# All downloads were successful.
# Compose the file from the parts.
set fac [new_CkFileAccess]

set success [CkFileAccess_ReassembleFile $fac "qa_output" "chunk" "dat" "qa_output/hamlet.xml"]
if {$success == 0} then {
    puts [CkFileAccess_lastErrorText $fac]
} else {
    puts "Success."
}

delete_CkTask $task1

delete_CkTask $task2

delete_CkTask $task3

delete_CkTask $task4

# Let's download in the regular way, and then compare files..
set success [CkHttp_Download $http $url "qa_output/hamletRegular.xml"]

# Compare files.
set bSame [CkFileAccess_FileContentsEqual $fac "qa_output/hamlet.xml" "qa_output/hamletRegular.xml"]
puts "bSame = $bSame"

delete_CkHttp $http
delete_CkHttpResponse $resp
delete_CkHttp $http1
delete_CkHttp $http2
delete_CkHttp $http3
delete_CkHttp $http4
delete_CkStringBuilder $sbRange
delete_CkFileAccess $fac