Sample code for 30+ languages & platforms
SQL Server

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 SQL Server Downloads

SQL Server
-- Important: See this note about string length limitations for strings returned by sp_OAMethod calls.
--
CREATE PROCEDURE ChilkatSample
AS
BEGIN
    DECLARE @hr int
    DECLARE @iTmp0 int
    -- Important: Do not use nvarchar(max).  See the warning about using nvarchar(max).
    DECLARE @sTmp0 nvarchar(4000)
    DECLARE @success int
    SELECT @success = 0

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

    DECLARE @http int
    EXEC @hr = sp_OACreate 'Chilkat.Http', @http OUT
    IF @hr <> 0
    BEGIN
        PRINT 'Failed to create ActiveX component'
        RETURN
    END

    -- First get the size of the file to be downloaded.
    DECLARE @url nvarchar(4000)
    SELECT @url = 'https://www.chilkatsoft.com/hamlet.xml'

    DECLARE @resp int
    EXEC @hr = sp_OACreate 'Chilkat.HttpResponse', @resp OUT

    EXEC sp_OAMethod @http, 'HttpNoBody', @success OUT, 'HEAD', @url, @resp
    IF @success = 0
      BEGIN
        EXEC sp_OAGetProperty @http, 'LastErrorText', @sTmp0 OUT
        PRINT @sTmp0
        EXEC @hr = sp_OADestroy @http
        EXEC @hr = sp_OADestroy @resp
        RETURN
      END

    DECLARE @remoteFileSize int
    EXEC sp_OAGetProperty @resp, 'ContentLength', @remoteFileSize OUT



    PRINT 'Downloading ' + @remoteFileSize + ' bytes...'

    -- Let's download in 4 chunks.
    -- (the last chunk will be whatever remains after the 1st 3 equal sized chunks)
    DECLARE @chunkSize int
    SELECT @chunkSize = @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..
    DECLARE @http1 int
    EXEC @hr = sp_OACreate 'Chilkat.Http', @http1 OUT

    DECLARE @http2 int
    EXEC @hr = sp_OACreate 'Chilkat.Http', @http2 OUT

    DECLARE @http3 int
    EXEC @hr = sp_OACreate 'Chilkat.Http', @http3 OUT

    DECLARE @http4 int
    EXEC @hr = sp_OACreate 'Chilkat.Http', @http4 OUT

    DECLARE @sbRange int
    EXEC @hr = sp_OACreate 'Chilkat.StringBuilder', @sbRange OUT

    EXEC sp_OAMethod @sbRange, 'SetString', @success OUT, 'bytes=<range-start>-<range-end>'
    DECLARE @numReplaced int
    EXEC sp_OAMethod @sbRange, 'ReplaceI', @numReplaced OUT, '<range-start>', 0
    EXEC sp_OAMethod @sbRange, 'ReplaceI', @numReplaced OUT, '<range-end>', @chunkSize - 1
    EXEC sp_OAMethod @sbRange, 'GetAsString', @sTmp0 OUT
    PRINT @sTmp0
    EXEC sp_OAMethod @sbRange, 'GetAsString', @sTmp0 OUT
    EXEC sp_OAMethod @http1, 'SetRequestHeader', NULL, 'Range', @sTmp0

    EXEC sp_OAMethod @sbRange, 'SetString', @success OUT, 'bytes=<range-start>-<range-end>'
    EXEC sp_OAMethod @sbRange, 'ReplaceI', @numReplaced OUT, '<range-start>', @chunkSize
    EXEC sp_OAMethod @sbRange, 'ReplaceI', @numReplaced OUT, '<range-end>', 2 * @chunkSize - 1
    EXEC sp_OAMethod @sbRange, 'GetAsString', @sTmp0 OUT
    PRINT @sTmp0
    EXEC sp_OAMethod @sbRange, 'GetAsString', @sTmp0 OUT
    EXEC sp_OAMethod @http2, 'SetRequestHeader', NULL, 'Range', @sTmp0

    EXEC sp_OAMethod @sbRange, 'SetString', @success OUT, 'bytes=<range-start>-<range-end>'
    EXEC sp_OAMethod @sbRange, 'ReplaceI', @numReplaced OUT, '<range-start>', 2 * @chunkSize
    EXEC sp_OAMethod @sbRange, 'ReplaceI', @numReplaced OUT, '<range-end>', 3 * @chunkSize - 1
    EXEC sp_OAMethod @sbRange, 'GetAsString', @sTmp0 OUT
    PRINT @sTmp0
    EXEC sp_OAMethod @sbRange, 'GetAsString', @sTmp0 OUT
    EXEC sp_OAMethod @http3, 'SetRequestHeader', NULL, 'Range', @sTmp0

    EXEC sp_OAMethod @sbRange, 'SetString', @success OUT, 'bytes=<range-start>-'
    EXEC sp_OAMethod @sbRange, 'ReplaceI', @numReplaced OUT, '<range-start>', 3 * @chunkSize
    EXEC sp_OAMethod @sbRange, 'GetAsString', @sTmp0 OUT
    PRINT @sTmp0
    EXEC sp_OAMethod @sbRange, 'GetAsString', @sTmp0 OUT
    EXEC sp_OAMethod @http4, 'SetRequestHeader', NULL, 'Range', @sTmp0

    -- Start each range download
    DECLARE @task1 int
    EXEC sp_OAMethod @http1, 'DownloadAsync', @task1 OUT, @url, 'qa_output/chunk1.dat'
    EXEC sp_OAMethod @task1, 'Run', @success OUT

    DECLARE @task2 int
    EXEC sp_OAMethod @http2, 'DownloadAsync', @task2 OUT, @url, 'qa_output/chunk2.dat'
    EXEC sp_OAMethod @task2, 'Run', @success OUT

    DECLARE @task3 int
    EXEC sp_OAMethod @http3, 'DownloadAsync', @task3 OUT, @url, 'qa_output/chunk3.dat'
    EXEC sp_OAMethod @task3, 'Run', @success OUT

    DECLARE @task4 int
    EXEC sp_OAMethod @http4, 'DownloadAsync', @task4 OUT, @url, 'qa_output/chunk4.dat'
    EXEC sp_OAMethod @task4, 'Run', @success OUT

    -- Wait for the downloads to complete.
    DECLARE @numLive int
    SELECT @numLive = 4
    WHILE @numLive > 0
      BEGIN
        SELECT @numLive = 0
        EXEC sp_OAGetProperty @task1, 'Live', @iTmp0 OUT
        IF @iTmp0 = 1
          BEGIN
            SELECT @numLive = @numLive + 1
          END
        EXEC sp_OAGetProperty @task2, 'Live', @iTmp0 OUT
        IF @iTmp0 = 1
          BEGIN
            SELECT @numLive = @numLive + 1
          END
        EXEC sp_OAGetProperty @task3, 'Live', @iTmp0 OUT
        IF @iTmp0 = 1
          BEGIN
            SELECT @numLive = @numLive + 1
          END
        EXEC sp_OAGetProperty @task4, 'Live', @iTmp0 OUT
        IF @iTmp0 = 1
          BEGIN
            SELECT @numLive = @numLive + 1
          END
        IF @numLive > 0
          BEGIN
            -- SleepMs is a convenience method to cause the caller to sleep for N millisec.
            -- It does not cause the given task to sleep..
            EXEC sp_OAMethod @task1, 'SleepMs', NULL, 10
          END
      END

    -- All should be downloaded now..
    -- Examine the result of each Download.
    DECLARE @numErrors int
    SELECT @numErrors = 0
    EXEC sp_OAMethod @task1, 'GetResultBool', @iTmp0 OUT
    IF @iTmp0 = 0
      BEGIN
        EXEC sp_OAGetProperty @task1, 'ResultErrorText', @sTmp0 OUT
        PRINT @sTmp0
        SELECT @numErrors = @numErrors + 1
      END
    EXEC sp_OAMethod @task2, 'GetResultBool', @iTmp0 OUT
    IF @iTmp0 = 0
      BEGIN
        EXEC sp_OAGetProperty @task2, 'ResultErrorText', @sTmp0 OUT
        PRINT @sTmp0
        SELECT @numErrors = @numErrors + 1
      END
    EXEC sp_OAMethod @task3, 'GetResultBool', @iTmp0 OUT
    IF @iTmp0 = 0
      BEGIN
        EXEC sp_OAGetProperty @task3, 'ResultErrorText', @sTmp0 OUT
        PRINT @sTmp0
        SELECT @numErrors = @numErrors + 1
      END
    EXEC sp_OAMethod @task4, 'GetResultBool', @iTmp0 OUT
    IF @iTmp0 = 0
      BEGIN
        EXEC sp_OAGetProperty @task4, 'ResultErrorText', @sTmp0 OUT
        PRINT @sTmp0
        SELECT @numErrors = @numErrors + 1
      END
    IF @numErrors > 0
      BEGIN
        EXEC @hr = sp_OADestroy @task1

        EXEC @hr = sp_OADestroy @task2

        EXEC @hr = sp_OADestroy @task3

        EXEC @hr = sp_OADestroy @task4

        EXEC @hr = sp_OADestroy @http
        EXEC @hr = sp_OADestroy @resp
        EXEC @hr = sp_OADestroy @http1
        EXEC @hr = sp_OADestroy @http2
        EXEC @hr = sp_OADestroy @http3
        EXEC @hr = sp_OADestroy @http4
        EXEC @hr = sp_OADestroy @sbRange
        RETURN
      END

    -- All downloads were successful.
    -- Compose the file from the parts.
    DECLARE @fac int
    EXEC @hr = sp_OACreate 'Chilkat.FileAccess', @fac OUT

    EXEC sp_OAMethod @fac, 'ReassembleFile', @success OUT, 'qa_output', 'chunk', 'dat', 'qa_output/hamlet.xml'
    IF @success = 0
      BEGIN
        EXEC sp_OAGetProperty @fac, 'LastErrorText', @sTmp0 OUT
        PRINT @sTmp0
      END
    ELSE
      BEGIN

        PRINT 'Success.'
      END

    EXEC @hr = sp_OADestroy @task1

    EXEC @hr = sp_OADestroy @task2

    EXEC @hr = sp_OADestroy @task3

    EXEC @hr = sp_OADestroy @task4

    -- Let's download in the regular way, and then compare files..
    EXEC sp_OAMethod @http, 'Download', @success OUT, @url, 'qa_output/hamletRegular.xml'

    -- Compare files.
    DECLARE @bSame int
    EXEC sp_OAMethod @fac, 'FileContentsEqual', @bSame OUT, 'qa_output/hamlet.xml', 'qa_output/hamletRegular.xml'

    PRINT 'bSame = ' + @bSame

    EXEC @hr = sp_OADestroy @http
    EXEC @hr = sp_OADestroy @resp
    EXEC @hr = sp_OADestroy @http1
    EXEC @hr = sp_OADestroy @http2
    EXEC @hr = sp_OADestroy @http3
    EXEC @hr = sp_OADestroy @http4
    EXEC @hr = sp_OADestroy @sbRange
    EXEC @hr = sp_OADestroy @fac


END
GO