Sample code for 30+ languages & platforms
PowerBuilder Requires Chilkat v11.7.0+

Box: Upload a Large File in Parts and Commit the Session

See more Box Examples

Run the reusable setup once per HttpCurl object, then select names and execute the operation. The setup is repeated here only to make this published example independently runnable. Resolve documents by name, then perform the explicit chunked-upload session, part, and commit loop. Supports 20 MB through 2 GB using int. Saves recovery information and honors bounded commit Retry-After.

See the Box API guide for setup and the complete workflow.

Chilkat PowerBuilder Downloads

PowerBuilder
integer li_rc
long li_Success
oleobject loo_Curl
oleobject loo_SetupCommand
long li_SetupReplacements
string ls_ParentName
string ls_FolderName
oleobject loo_AuthorizedCommand1
long li_HeaderReplacement1
string ls_LocalPath
oleobject loo_File
string ls_SizeString
oleobject loo_SizeText
oleobject loo_SizeMatch
long li_FileSize
oleobject loo_Http
oleobject loo_Response
oleobject loo_Request
oleobject loo_Session
long li_PartSize
string ls_UploadUrl
string ls_CommitUrl
oleobject loo_Hash
string ls_FileDigest
oleobject loo_Commit
oleobject loo_PartResponse
oleobject loo_Chunk
oleobject loo_Range
oleobject loo_DigestHeader
long li_Offset
long li_PartIndex
long li_PartLength
long li_LastByte
string ls_PartDigest
long li_Attempts
long li_WaitSeconds
oleobject loo_RetryHeader
oleobject loo_RetryMatch
long li_Completed

li_Success = 0

//  Requires Chilkat 11.7.0 or later and a previously unlocked Chilkat bundle.
//  See Global Unlock Sample.
//  Guide: Box API Guide.

li_Success = 0

//  BEGIN ONE-TIME SETUP
//  This block is repeated so each published example can run on its own.
//  In an application, run the setup example once and pass the SAME curl object
//  to operation code below. Do not recreate it for every operation.
//  See Reusable Box HttpCurl Setup.
loo_Curl = create oleobject
li_rc = loo_Curl.ConnectToNewObject("Chilkat.HttpCurl")
if li_rc < 0 then
    destroy loo_Curl
    MessageBox("Error","Connecting to COM object failed")
    return
end if
loo_Curl.SetVar("access_token","BOX_ACCESS_TOKEN")
loo_Curl.SetVar("parent_name","/")
//  Box's fixed root ID is an implementation detail; callers supply names.
loo_Curl.SetVar("parent_id","0")
loo_SetupCommand = create oleobject
li_rc = loo_SetupCommand.ConnectToNewObject("Chilkat.StringBuilder")

//  Register findParent; registration sends no request.
loo_SetupCommand.SetString("curl -X GET ~"https://api.box.com/2.0/folders/0/items?limit=1000&fields=id,type,name~" -H ~"Authorization: Bearer ACCESS_TOKEN~"")
li_SetupReplacements = loo_SetupCommand.Replace("ACCESS_TOKEN",loo_Curl.GetVar("access_token"))
li_Success = loo_Curl.AddFunction("findParent",loo_SetupCommand.GetAsString())
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

//  Register findFolder; registration sends no request.
loo_SetupCommand.SetString("curl -X GET ~"https://api.box.com/2.0/folders/{{parent_id}}/items?limit=1000&fields=id,type,name~" -H ~"Authorization: Bearer ACCESS_TOKEN~"")
li_SetupReplacements = loo_SetupCommand.Replace("ACCESS_TOKEN",loo_Curl.GetVar("access_token"))
li_Success = loo_Curl.AddFunction("findFolder",loo_SetupCommand.GetAsString())
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

//  Register findFile; registration sends no request.
loo_SetupCommand.SetString("curl -X GET ~"https://api.box.com/2.0/folders/{{folder_id}}/items?limit=1000&fields=id,type,name~" -H ~"Authorization: Bearer ACCESS_TOKEN~"")
li_SetupReplacements = loo_SetupCommand.Replace("ACCESS_TOKEN",loo_Curl.GetVar("access_token"))
li_Success = loo_Curl.AddFunction("findFile",loo_SetupCommand.GetAsString())
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

//  Register findDestination; registration sends no request.
loo_SetupCommand.SetString("curl -X GET ~"https://api.box.com/2.0/folders/0/items?limit=1000&fields=id,type,name~" -H ~"Authorization: Bearer ACCESS_TOKEN~"")
li_SetupReplacements = loo_SetupCommand.Replace("ACCESS_TOKEN",loo_Curl.GetVar("access_token"))
li_Success = loo_Curl.AddFunction("findDestination",loo_SetupCommand.GetAsString())
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

//  Register getFileDetails; registration sends no request.
loo_SetupCommand.SetString("curl -X GET ~"https://api.box.com/2.0/files/{{file_id}}?fields=id,sha1,file_version~" -H ~"Authorization: Bearer ACCESS_TOKEN~"")
li_SetupReplacements = loo_SetupCommand.Replace("ACCESS_TOKEN",loo_Curl.GetVar("access_token"))
li_Success = loo_Curl.AddFunction("getFileDetails",loo_SetupCommand.GetAsString())
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

//  Register getEnterprise; registration sends no request.
loo_SetupCommand.SetString("curl -X GET ~"https://api.box.com/2.0/users/me?fields=enterprise~" -H ~"Authorization: Bearer ACCESS_TOKEN~"")
li_SetupReplacements = loo_SetupCommand.Replace("ACCESS_TOKEN",loo_Curl.GetVar("access_token"))
li_Success = loo_Curl.AddFunction("getEnterprise",loo_SetupCommand.GetAsString())
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

//  Register output rules once. Chilkat 11.7.0 evaluates {{name}} variables
//  when extracting each output, using the current value at that time.
li_Success = loo_Curl.AddOutput2("findParent","entries","name","{{parent_name}}",1,"id","parent_id")
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

li_Success = loo_Curl.AddOutput2("findFolder","entries","name","{{folder_name}}",1,"id","folder_id")
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

li_Success = loo_Curl.AddOutput2("findFile","entries","name","{{file_name}}",1,"id","file_id")
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

li_Success = loo_Curl.AddOutput2("findDestination","entries","name","{{destination_name}}",1,"id","destination_id")
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

li_Success = loo_Curl.AddOutput("getFileDetails","file_version.id","file_version_id")
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

li_Success = loo_Curl.AddOutput("getFileDetails","sha1","file_sha1")
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

li_Success = loo_Curl.AddOutput("getEnterprise","enterprise.id","enterprise_id")
if li_Success <> 1 then
    Write-Debug loo_Curl.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    return
end if

//  END ONE-TIME SETUP
//  The name lookups below assume each listing fits in one page (1000 items).
//  Parent and destination names select root children; folder_name selects a child
//  of parent_name. "/" selects the root parent. These are not arbitrary paths.

//  BEGIN OPERATION -- keep the configured object when reusing it.
//  Inputs are names. Retain cached IDs when the selected names have not changed.
ls_ParentName = "/"
ls_FolderName = "documents"
if (loo_Curl.GetVar("parent_name") <> ls_ParentName) OR ((ls_ParentName = "/") AND (loo_Curl.VarDefined("parent_id") = 0)) then
    loo_Curl.SetVar("parent_name",ls_ParentName)
    loo_Curl.ClearVar("parent_id")
    loo_Curl.ClearVar("folder_id")
    loo_Curl.ClearVar("file_id")
    loo_Curl.ClearVar("file_version_id")
    loo_Curl.ClearVar("file_sha1")
    if ls_ParentName = "/" then
        loo_Curl.SetVar("parent_id","0")
    end if

end if

if loo_Curl.GetVar("folder_name") <> ls_FolderName then
    loo_Curl.SetVar("folder_name",ls_FolderName)
    loo_Curl.ClearVar("folder_id")
    loo_Curl.ClearVar("file_id")
    loo_Curl.ClearVar("file_version_id")
    loo_Curl.ClearVar("file_sha1")
end if

//  Resolve the named destination first. HttpCurl does not expand variables
//  inside multipart -F attributes in the tested build, so this upload is two phases.
loo_Curl.ClearTargetOutput("*")
loo_AuthorizedCommand1 = create oleobject
li_rc = loo_AuthorizedCommand1.ConnectToNewObject("Chilkat.StringBuilder")

loo_AuthorizedCommand1.SetString("curl -X GET ~"https://api.box.com/2.0/folders/{{folder_id}}?fields=id~" -H ~"Authorization: Bearer {{access_token}}~"")
li_HeaderReplacement1 = loo_AuthorizedCommand1.Replace("{{access_token}}",loo_Curl.GetVar("access_token"))
li_Success = loo_Curl.DoYourThing(loo_AuthorizedCommand1.GetAsString())
if (li_Success <> 1) OR (loo_Curl.StatusCode <> 200) then
    Write-Debug "Box request failed. HTTP status: " + string(loo_Curl.StatusCode)
    Write-Debug "Failure reason: " + string(loo_Curl.FailReason)
    Write-Debug loo_Curl.ResponseBodyStr
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    return
end if

//  This example limits size to 2 GB so offsets fit all generated languages' integers.
//  The source must not change during hashing or upload. Create qa_output first.
ls_LocalPath = "qa_data/large-document.zip"
loo_File = create oleobject
li_rc = loo_File.ConnectToNewObject("Chilkat.FileAccess")

//  Check the decimal size before converting so an oversized file cannot overflow int.
ls_SizeString = loo_File.FileSizeStr(ls_LocalPath)
if loo_File.LastMethodSuccess <> 1 then
    Write-Debug loo_File.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    return
end if

loo_SizeText = create oleobject
li_rc = loo_SizeText.ConnectToNewObject("Chilkat.StringBuilder")

loo_SizeText.SetString(ls_SizeString)
loo_SizeMatch = create oleobject
li_rc = loo_SizeMatch.ConnectToNewObject("Chilkat.JsonObject")

if loo_SizeText.RegexMatch("^([0-9]{1,9}|1[0-9]{9}|2000000000)$",loo_SizeMatch,100) <> 1 then
    Write-Debug "Choose an input between 20 MB and 2 GB for this example."
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    return
end if

li_FileSize = Long(ls_SizeString)
if li_FileSize < 20971520 then
    Write-Debug "Choose an input between 20 MB and 2 GB for this example."
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    return
end if

loo_Http = create oleobject
li_rc = loo_Http.ConnectToNewObject("Chilkat.Http")

loo_Http.AuthToken = loo_Curl.GetVar("access_token")
loo_Response = create oleobject
li_rc = loo_Response.ConnectToNewObject("Chilkat.HttpResponse")

loo_Request = create oleobject
li_rc = loo_Request.ConnectToNewObject("Chilkat.JsonObject")

loo_Request.UpdateString("folder_id",loo_Curl.GetVar("folder_id"))
loo_Request.UpdateString("file_name","large-document.zip")
loo_Request.UpdateInt("file_size",li_FileSize)
li_Success = loo_Http.HttpJson("POST","https://upload.box.com/api/2.0/files/upload_sessions",loo_Request,"application/json",loo_Response)
if li_Success <> 1 then
    Write-Debug loo_Http.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    destroy loo_Http
    destroy loo_Response
    destroy loo_Request
    return
end if

if loo_Response.StatusCode <> 201 then
    Write-Debug loo_Response.BodyStr
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    destroy loo_Http
    destroy loo_Response
    destroy loo_Request
    return
end if

loo_Session = create oleobject
li_rc = loo_Session.ConnectToNewObject("Chilkat.JsonObject")

li_Success = loo_Session.Load(loo_Response.BodyStr)
if li_Success <> 1 then
    Write-Debug loo_Session.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    destroy loo_Http
    destroy loo_Response
    destroy loo_Request
    destroy loo_Session
    return
end if

li_Success = loo_Session.WriteFile("qa_output/box_upload_session.json")
if li_Success <> 1 then
    Write-Debug loo_Session.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    destroy loo_Http
    destroy loo_Response
    destroy loo_Request
    destroy loo_Session
    return
end if

li_PartSize = loo_Session.IntOf("part_size")
ls_UploadUrl = loo_Session.StringOf("session_endpoints.upload_part")
ls_CommitUrl = loo_Session.StringOf("session_endpoints.commit")
if (li_PartSize <= 0) OR (ls_UploadUrl = "") OR (ls_CommitUrl = "") then
    Write-Debug "Invalid session response."
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    destroy loo_Http
    destroy loo_Response
    destroy loo_Request
    destroy loo_Session
    return
end if

loo_Hash = create oleobject
li_rc = loo_Hash.ConnectToNewObject("Chilkat.Crypt2")

loo_Hash.HashAlgorithm = "sha1"
loo_Hash.EncodingMode = "base64"
ls_FileDigest = loo_Hash.HashFileENC(ls_LocalPath)
if loo_Hash.LastMethodSuccess <> 1 then
    Write-Debug loo_Hash.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    destroy loo_Http
    destroy loo_Response
    destroy loo_Request
    destroy loo_Session
    destroy loo_Hash
    return
end if

li_Success = loo_File.FileOpen(ls_LocalPath,2147483648,1,3,0)
if li_Success <> 1 then
    Write-Debug loo_File.LastErrorText
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    destroy loo_Http
    destroy loo_Response
    destroy loo_Request
    destroy loo_Session
    destroy loo_Hash
    return
end if

loo_Commit = create oleobject
li_rc = loo_Commit.ConnectToNewObject("Chilkat.JsonObject")

loo_PartResponse = create oleobject
li_rc = loo_PartResponse.ConnectToNewObject("Chilkat.JsonObject")

loo_Chunk = create oleobject
li_rc = loo_Chunk.ConnectToNewObject("Chilkat.BinData")

loo_Range = create oleobject
li_rc = loo_Range.ConnectToNewObject("Chilkat.StringBuilder")

loo_DigestHeader = create oleobject
li_rc = loo_DigestHeader.ConnectToNewObject("Chilkat.StringBuilder")

li_Offset = 0
li_PartIndex = 0

do while li_Offset < li_FileSize
    loo_Chunk.Clear()
    li_Success = loo_File.FileReadBd(li_PartSize,loo_Chunk)
    if li_Success <> 1 then
        Write-Debug loo_File.LastErrorText
        loo_File.FileClose()
        destroy loo_Curl
        destroy loo_SetupCommand
        destroy loo_AuthorizedCommand1
        destroy loo_File
        destroy loo_SizeText
        destroy loo_SizeMatch
        destroy loo_Http
        destroy loo_Response
        destroy loo_Request
        destroy loo_Session
        destroy loo_Hash
        destroy loo_Commit
        destroy loo_PartResponse
        destroy loo_Chunk
        destroy loo_Range
        destroy loo_DigestHeader
        return
    end if

    li_PartLength = loo_Chunk.NumBytes
    if li_PartLength <= 0 then
        Write-Debug "Unexpected end of file."
        loo_File.FileClose()
        destroy loo_Curl
        destroy loo_SetupCommand
        destroy loo_AuthorizedCommand1
        destroy loo_File
        destroy loo_SizeText
        destroy loo_SizeMatch
        destroy loo_Http
        destroy loo_Response
        destroy loo_Request
        destroy loo_Session
        destroy loo_Hash
        destroy loo_Commit
        destroy loo_PartResponse
        destroy loo_Chunk
        destroy loo_Range
        destroy loo_DigestHeader
        return
    end if

    li_LastByte = li_Offset + (li_PartLength - 1)
    loo_Range.Clear()
    loo_Range.Append("bytes ")
    loo_Range.AppendInt(li_Offset)
    loo_Range.Append("-")
    loo_Range.AppendInt(li_LastByte)
    loo_Range.Append("/")
    loo_Range.AppendInt(li_FileSize)
    ls_PartDigest = loo_Hash.HashBdENC(loo_Chunk)
    if loo_Hash.LastMethodSuccess <> 1 then
        Write-Debug loo_Hash.LastErrorText
        loo_File.FileClose()
        destroy loo_Curl
        destroy loo_SetupCommand
        destroy loo_AuthorizedCommand1
        destroy loo_File
        destroy loo_SizeText
        destroy loo_SizeMatch
        destroy loo_Http
        destroy loo_Response
        destroy loo_Request
        destroy loo_Session
        destroy loo_Hash
        destroy loo_Commit
        destroy loo_PartResponse
        destroy loo_Chunk
        destroy loo_Range
        destroy loo_DigestHeader
        return
    end if

    loo_DigestHeader.SetString("sha=")
    loo_DigestHeader.Append(ls_PartDigest)
    loo_Http.SetRequestHeader("Digest",loo_DigestHeader.GetAsString())
    loo_Http.SetRequestHeader("Content-Range",loo_Range.GetAsString())
    li_Success = loo_Http.HttpBd("PUT",ls_UploadUrl,loo_Chunk,"application/octet-stream",loo_Response)
    if (li_Success <> 1) OR (loo_Response.StatusCode <> 200) then
        Write-Debug "Part failed. HTTP status: " + string(loo_Response.StatusCode)
        Write-Debug loo_Response.BodyStr
        Write-Debug "Inspect the saved session and server list_parts before retrying this part."
        loo_File.FileClose()
        destroy loo_Curl
        destroy loo_SetupCommand
        destroy loo_AuthorizedCommand1
        destroy loo_File
        destroy loo_SizeText
        destroy loo_SizeMatch
        destroy loo_Http
        destroy loo_Response
        destroy loo_Request
        destroy loo_Session
        destroy loo_Hash
        destroy loo_Commit
        destroy loo_PartResponse
        destroy loo_Chunk
        destroy loo_Range
        destroy loo_DigestHeader
        return
    end if

    li_Success = loo_PartResponse.Load(loo_Response.BodyStr)
    if li_Success <> 1 then
        loo_File.FileClose()
        destroy loo_Curl
        destroy loo_SetupCommand
        destroy loo_AuthorizedCommand1
        destroy loo_File
        destroy loo_SizeText
        destroy loo_SizeMatch
        destroy loo_Http
        destroy loo_Response
        destroy loo_Request
        destroy loo_Session
        destroy loo_Hash
        destroy loo_Commit
        destroy loo_PartResponse
        destroy loo_Chunk
        destroy loo_Range
        destroy loo_DigestHeader
        return
    end if

    loo_Commit.I = li_PartIndex
    //  Copy the server's acknowledged part fields into the commit manifest.
    loo_Commit.UpdateString("parts[i].part_id",loo_PartResponse.StringOf("part.part_id"))
    loo_Commit.UpdateInt("parts[i].offset",loo_PartResponse.IntOf("part.offset"))
    loo_Commit.UpdateInt("parts[i].size",loo_PartResponse.IntOf("part.size"))
    loo_Commit.UpdateString("parts[i].sha1",loo_PartResponse.StringOf("part.sha1"))
    li_Success = loo_Commit.WriteFile("qa_output/box_upload_parts.json")
    if li_Success <> 1 then
        Write-Debug loo_Commit.LastErrorText
        loo_File.FileClose()
        destroy loo_Curl
        destroy loo_SetupCommand
        destroy loo_AuthorizedCommand1
        destroy loo_File
        destroy loo_SizeText
        destroy loo_SizeMatch
        destroy loo_Http
        destroy loo_Response
        destroy loo_Request
        destroy loo_Session
        destroy loo_Hash
        destroy loo_Commit
        destroy loo_PartResponse
        destroy loo_Chunk
        destroy loo_Range
        destroy loo_DigestHeader
        return
    end if

    li_Offset = li_Offset + li_PartLength
    li_PartIndex = li_PartIndex + 1
loop
loo_File.FileClose()
loo_Http.RemoveRequestHeader("Content-Range")
loo_DigestHeader.SetString("sha=")
loo_DigestHeader.Append(ls_FileDigest)
loo_Http.SetRequestHeader("Digest",loo_DigestHeader.GetAsString())
li_Attempts = 0

loo_RetryHeader = create oleobject
li_rc = loo_RetryHeader.ConnectToNewObject("Chilkat.StringBuilder")

loo_RetryMatch = create oleobject
li_rc = loo_RetryMatch.ConnectToNewObject("Chilkat.JsonObject")

li_Completed = 0
do while (li_Attempts < 10) AND (li_Completed = 0)
    li_Success = loo_Http.HttpJson("POST",ls_CommitUrl,loo_Commit,"application/json",loo_Response)
    if li_Success <> 1 then
        Write-Debug "Commit failed: " + string(loo_Response.StatusCode)
        Write-Debug loo_Response.BodyStr
        destroy loo_Curl
        destroy loo_SetupCommand
        destroy loo_AuthorizedCommand1
        destroy loo_File
        destroy loo_SizeText
        destroy loo_SizeMatch
        destroy loo_Http
        destroy loo_Response
        destroy loo_Request
        destroy loo_Session
        destroy loo_Hash
        destroy loo_Commit
        destroy loo_PartResponse
        destroy loo_Chunk
        destroy loo_Range
        destroy loo_DigestHeader
        destroy loo_RetryHeader
        destroy loo_RetryMatch
        return
    end if

    if loo_Response.StatusCode = 201 then
        li_Completed = 1
    else
        if loo_Response.StatusCode <> 202 then
            Write-Debug "Unexpected commit status: " + string(loo_Response.StatusCode)
            destroy loo_Curl
            destroy loo_SetupCommand
            destroy loo_AuthorizedCommand1
            destroy loo_File
            destroy loo_SizeText
            destroy loo_SizeMatch
            destroy loo_Http
            destroy loo_Response
            destroy loo_Request
            destroy loo_Session
            destroy loo_Hash
            destroy loo_Commit
            destroy loo_PartResponse
            destroy loo_Chunk
            destroy loo_Range
            destroy loo_DigestHeader
            destroy loo_RetryHeader
            destroy loo_RetryMatch
            return
        end if

        //  Box's asynchronous commit response supplies Retry-After in seconds.
        loo_RetryHeader.SetString(loo_Response.GetHeaderField("Retry-After"))
        if loo_RetryHeader.RegexMatch("^[0-9]{1,2}$",loo_RetryMatch,100) <> 1 then
            Write-Debug "Commit pending. Preserve the session and inspect Retry-After before resuming: " + loo_RetryHeader.GetAsString()
            destroy loo_Curl
            destroy loo_SetupCommand
            destroy loo_AuthorizedCommand1
            destroy loo_File
            destroy loo_SizeText
            destroy loo_SizeMatch
            destroy loo_Http
            destroy loo_Response
            destroy loo_Request
            destroy loo_Session
            destroy loo_Hash
            destroy loo_Commit
            destroy loo_PartResponse
            destroy loo_Chunk
            destroy loo_Range
            destroy loo_DigestHeader
            destroy loo_RetryHeader
            destroy loo_RetryMatch
            return
        end if

        li_WaitSeconds = Long(loo_RetryHeader.GetAsString())
        if (li_WaitSeconds < 1) OR (li_WaitSeconds > 60) then
            Write-Debug "Commit pending. Resume commit after Retry-After: " + loo_Response.GetHeaderField("Retry-After")
            destroy loo_Curl
            destroy loo_SetupCommand
            destroy loo_AuthorizedCommand1
            destroy loo_File
            destroy loo_SizeText
            destroy loo_SizeMatch
            destroy loo_Http
            destroy loo_Response
            destroy loo_Request
            destroy loo_Session
            destroy loo_Hash
            destroy loo_Commit
            destroy loo_PartResponse
            destroy loo_Chunk
            destroy loo_Range
            destroy loo_DigestHeader
            destroy loo_RetryHeader
            destroy loo_RetryMatch
            return
        end if

        loo_Http.SleepMs(li_WaitSeconds * 1000)
    end if

    li_Attempts = li_Attempts + 1
loop
if li_Completed = 0 then
    Write-Debug "Commit still pending; retain session and part manifests to resume commit later."
    destroy loo_Curl
    destroy loo_SetupCommand
    destroy loo_AuthorizedCommand1
    destroy loo_File
    destroy loo_SizeText
    destroy loo_SizeMatch
    destroy loo_Http
    destroy loo_Response
    destroy loo_Request
    destroy loo_Session
    destroy loo_Hash
    destroy loo_Commit
    destroy loo_PartResponse
    destroy loo_Chunk
    destroy loo_Range
    destroy loo_DigestHeader
    destroy loo_RetryHeader
    destroy loo_RetryMatch
    return
end if

Write-Debug "Upload committed: " + loo_Response.BodyStr


destroy loo_Curl
destroy loo_SetupCommand
destroy loo_AuthorizedCommand1
destroy loo_File
destroy loo_SizeText
destroy loo_SizeMatch
destroy loo_Http
destroy loo_Response
destroy loo_Request
destroy loo_Session
destroy loo_Hash
destroy loo_Commit
destroy loo_PartResponse
destroy loo_Chunk
destroy loo_Range
destroy loo_DigestHeader
destroy loo_RetryHeader
destroy loo_RetryMatch