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

Swift

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)")

}