Swift
Swift
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 Swift Downloads
func chilkatTest() {
var success: Bool = false
// This requires the Chilkat API to have been previously unlocked.
// See Global Unlock Sample for sample code.
let http = CkoHttp()!
// First get the size of the file to be downloaded.
var url: String? = "https://www.chilkatsoft.com/hamlet.xml"
let resp = CkoHttpResponse()!
success = http.httpNoBody(verb: "HEAD", url: url, response: resp)
if success == false {
print("\(http.lastErrorText!)")
return
}
var remoteFileSize: Int = resp.contentLength
print("Downloading \(remoteFileSize) bytes...")
// Let's download in 4 chunks.
// (the last chunk will be whatever remains after the 1st 3 equal sized chunks)
var chunkSize: Int = 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..
let http1 = CkoHttp()!
let http2 = CkoHttp()!
let http3 = CkoHttp()!
let http4 = CkoHttp()!
let sbRange = CkoStringBuilder()!
sbRange.setString(value: "bytes=<range-start>-<range-end>")
var numReplaced: Int = sbRange.replaceI(value: "<range-start>", replacement: 0).intValue
numReplaced = sbRange.replaceI(value: "<range-end>", replacement: chunkSize - 1).intValue
print("\(sbRange.getAsString()!)")
http1.setRequestHeader(name: "Range", value: sbRange.getAsString())
sbRange.setString(value: "bytes=<range-start>-<range-end>")
numReplaced = sbRange.replaceI(value: "<range-start>", replacement: chunkSize).intValue
numReplaced = sbRange.replaceI(value: "<range-end>", replacement: 2 * chunkSize - 1).intValue
print("\(sbRange.getAsString()!)")
http2.setRequestHeader(name: "Range", value: sbRange.getAsString())
sbRange.setString(value: "bytes=<range-start>-<range-end>")
numReplaced = sbRange.replaceI(value: "<range-start>", replacement: 2 * chunkSize).intValue
numReplaced = sbRange.replaceI(value: "<range-end>", replacement: 3 * chunkSize - 1).intValue
print("\(sbRange.getAsString()!)")
http3.setRequestHeader(name: "Range", value: sbRange.getAsString())
sbRange.setString(value: "bytes=<range-start>-")
numReplaced = sbRange.replaceI(value: "<range-start>", replacement: 3 * chunkSize).intValue
print("\(sbRange.getAsString()!)")
http4.setRequestHeader(name: "Range", value: sbRange.getAsString())
// Start each range download
var task1: CkoTask? = http1.downloadAsync(url: url, saveToPath: "qa_output/chunk1.dat")
task1!.run()
var task2: CkoTask? = http2.downloadAsync(url: url, saveToPath: "qa_output/chunk2.dat")
task2!.run()
var task3: CkoTask? = http3.downloadAsync(url: url, saveToPath: "qa_output/chunk3.dat")
task3!.run()
var task4: CkoTask? = http4.downloadAsync(url: url, saveToPath: "qa_output/chunk4.dat")
task4!.run()
// Wait for the downloads to complete.
var numLive: Int = 4
while numLive > 0 {
numLive = 0
if task1!.live == true {
numLive = numLive + 1
}
if task2!.live == true {
numLive = numLive + 1
}
if task3!.live == true {
numLive = numLive + 1
}
if task4!.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(numMs: 10)
}
}
// All should be downloaded now..
// Examine the result of each Download.
var numErrors: Int = 0
if task1!.getResultBool() == false {
print("\(task1!.resultErrorText!)")
numErrors = numErrors + 1
}
if task2!.getResultBool() == false {
print("\(task2!.resultErrorText!)")
numErrors = numErrors + 1
}
if task3!.getResultBool() == false {
print("\(task3!.resultErrorText!)")
numErrors = numErrors + 1
}
if task4!.getResultBool() == false {
print("\(task4!.resultErrorText!)")
numErrors = numErrors + 1
}
if numErrors > 0 {
task1 = nil
task2 = nil
task3 = nil
task4 = nil
return
}
// All downloads were successful.
// Compose the file from the parts.
let fac = CkoFileAccess()!
success = fac.reassembleFile(partsDirPath: "qa_output", partPrefix: "chunk", partExtension: "dat", reassembledFilename: "qa_output/hamlet.xml")
if success == false {
print("\(fac.lastErrorText!)")
}
else {
print("Success.")
}
task1 = nil
task2 = nil
task3 = nil
task4 = nil
// Let's download in the regular way, and then compare files..
success = http.download(url: url, saveToPath: "qa_output/hamletRegular.xml")
// Compare files.
var bSame: Bool = fac.fileContentsEqual(path1: "qa_output/hamlet.xml", path2: "qa_output/hamletRegular.xml")
print("bSame = \(bSame)")
}