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