Tcl Requires Chilkat v11.7.0+
Tcl
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 Tcl Downloads
load ./chilkat.dll
set success 0
# Requires Chilkat 11.7.0 or later and a previously unlocked Chilkat bundle.
# See Global Unlock Sample.
# Guide: Box API Guide.
set 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.
set curl [new_CkHttpCurl]
CkHttpCurl_SetVar $curl "access_token" "BOX_ACCESS_TOKEN"
CkHttpCurl_SetVar $curl "parent_name" "/"
# Box's fixed root ID is an implementation detail; callers supply names.
CkHttpCurl_SetVar $curl "parent_id" "0"
set setupCommand [new_CkStringBuilder]
# Register findParent; registration sends no request.
CkStringBuilder_SetString $setupCommand "curl -X GET \"https://api.box.com/2.0/folders/0/items?limit=1000&fields=id,type,name\" -H \"Authorization: Bearer ACCESS_TOKEN\""
set setupReplacements [CkStringBuilder_Replace $setupCommand "ACCESS_TOKEN" [CkHttpCurl_getVar $curl "access_token"]]
set success [CkHttpCurl_AddFunction $curl "findParent" [CkStringBuilder_getAsString $setupCommand]]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
# Register findFolder; registration sends no request.
CkStringBuilder_SetString $setupCommand "curl -X GET \"https://api.box.com/2.0/folders/{{parent_id}}/items?limit=1000&fields=id,type,name\" -H \"Authorization: Bearer ACCESS_TOKEN\""
set setupReplacements [CkStringBuilder_Replace $setupCommand "ACCESS_TOKEN" [CkHttpCurl_getVar $curl "access_token"]]
set success [CkHttpCurl_AddFunction $curl "findFolder" [CkStringBuilder_getAsString $setupCommand]]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
# Register findFile; registration sends no request.
CkStringBuilder_SetString $setupCommand "curl -X GET \"https://api.box.com/2.0/folders/{{folder_id}}/items?limit=1000&fields=id,type,name\" -H \"Authorization: Bearer ACCESS_TOKEN\""
set setupReplacements [CkStringBuilder_Replace $setupCommand "ACCESS_TOKEN" [CkHttpCurl_getVar $curl "access_token"]]
set success [CkHttpCurl_AddFunction $curl "findFile" [CkStringBuilder_getAsString $setupCommand]]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
# Register findDestination; registration sends no request.
CkStringBuilder_SetString $setupCommand "curl -X GET \"https://api.box.com/2.0/folders/0/items?limit=1000&fields=id,type,name\" -H \"Authorization: Bearer ACCESS_TOKEN\""
set setupReplacements [CkStringBuilder_Replace $setupCommand "ACCESS_TOKEN" [CkHttpCurl_getVar $curl "access_token"]]
set success [CkHttpCurl_AddFunction $curl "findDestination" [CkStringBuilder_getAsString $setupCommand]]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
# Register getFileDetails; registration sends no request.
CkStringBuilder_SetString $setupCommand "curl -X GET \"https://api.box.com/2.0/files/{{file_id}}?fields=id,sha1,file_version\" -H \"Authorization: Bearer ACCESS_TOKEN\""
set setupReplacements [CkStringBuilder_Replace $setupCommand "ACCESS_TOKEN" [CkHttpCurl_getVar $curl "access_token"]]
set success [CkHttpCurl_AddFunction $curl "getFileDetails" [CkStringBuilder_getAsString $setupCommand]]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
# Register getEnterprise; registration sends no request.
CkStringBuilder_SetString $setupCommand "curl -X GET \"https://api.box.com/2.0/users/me?fields=enterprise\" -H \"Authorization: Bearer ACCESS_TOKEN\""
set setupReplacements [CkStringBuilder_Replace $setupCommand "ACCESS_TOKEN" [CkHttpCurl_getVar $curl "access_token"]]
set success [CkHttpCurl_AddFunction $curl "getEnterprise" [CkStringBuilder_getAsString $setupCommand]]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
# Register output rules once. Chilkat 11.7.0 evaluates {{name}} variables
# when extracting each output, using the current value at that time.
set success [CkHttpCurl_AddOutput2 $curl "findParent" "entries" "name" "{{parent_name}}" 1 "id" "parent_id"]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
set success [CkHttpCurl_AddOutput2 $curl "findFolder" "entries" "name" "{{folder_name}}" 1 "id" "folder_id"]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
set success [CkHttpCurl_AddOutput2 $curl "findFile" "entries" "name" "{{file_name}}" 1 "id" "file_id"]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
set success [CkHttpCurl_AddOutput2 $curl "findDestination" "entries" "name" "{{destination_name}}" 1 "id" "destination_id"]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
set success [CkHttpCurl_AddOutput $curl "getFileDetails" "file_version.id" "file_version_id"]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
set success [CkHttpCurl_AddOutput $curl "getFileDetails" "sha1" "file_sha1"]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
set success [CkHttpCurl_AddOutput $curl "getEnterprise" "enterprise.id" "enterprise_id"]
if {$success != 1} then {
puts [CkHttpCurl_lastErrorText $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
exit
}
# 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.
set parentName "/"
set folderName "documents"
if {expr [[CkHttpCurl_getVar $curl "parent_name"] != $parentName] || [expr [$parentName == "/"] && [[CkHttpCurl_VarDefined $curl "parent_id"] == 0]]} then {
CkHttpCurl_SetVar $curl "parent_name" $parentName
CkHttpCurl_ClearVar $curl "parent_id"
CkHttpCurl_ClearVar $curl "folder_id"
CkHttpCurl_ClearVar $curl "file_id"
CkHttpCurl_ClearVar $curl "file_version_id"
CkHttpCurl_ClearVar $curl "file_sha1"
if {$parentName == "/"} then {
CkHttpCurl_SetVar $curl "parent_id" "0"
}
}
if {[CkHttpCurl_getVar $curl "folder_name"] != $folderName} then {
CkHttpCurl_SetVar $curl "folder_name" $folderName
CkHttpCurl_ClearVar $curl "folder_id"
CkHttpCurl_ClearVar $curl "file_id"
CkHttpCurl_ClearVar $curl "file_version_id"
CkHttpCurl_ClearVar $curl "file_sha1"
}
# Resolve the named destination first. HttpCurl does not expand variables
# inside multipart -F attributes in the tested build, so this upload is two phases.
CkHttpCurl_ClearTargetOutput $curl "*"
set authorizedCommand1 [new_CkStringBuilder]
CkStringBuilder_SetString $authorizedCommand1 "curl -X GET \"https://api.box.com/2.0/folders/{{folder_id}}?fields=id\" -H \"Authorization: Bearer {{access_token}}\""
set headerReplacement1 [CkStringBuilder_Replace $authorizedCommand1 "{{access_token}}" [CkHttpCurl_getVar $curl "access_token"]]
set success [CkHttpCurl_DoYourThing $curl [CkStringBuilder_getAsString $authorizedCommand1]]
if {expr [$success != 1] || [[CkHttpCurl_get_StatusCode $curl] != 200]} then {
puts "Box request failed. HTTP status: [CkHttpCurl_get_StatusCode $curl]"
puts "Failure reason: [CkHttpCurl_get_FailReason $curl]"
puts [CkHttpCurl_responseBodyStr $curl]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
exit
}
# 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.
set localPath "qa_data/large-document.zip"
set file [new_CkFileAccess]
# Check the decimal size before converting so an oversized file cannot overflow int.
set sizeString [CkFileAccess_fileSizeStr $file $localPath]
if {[CkFileAccess_get_LastMethodSuccess $file] != 1} then {
puts [CkFileAccess_lastErrorText $file]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
exit
}
set sizeText [new_CkStringBuilder]
CkStringBuilder_SetString $sizeText $sizeString
set sizeMatch [new_CkJsonObject]
if {[CkStringBuilder_RegexMatch $sizeText "^([0-9]{1,9}|1[0-9]{9}|2000000000)$" $sizeMatch 100] != 1} then {
puts "Choose an input between 20 MB and 2 GB for this example."
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
exit
}
set fileSize $sizeString
if {$fileSize < 20971520} then {
puts "Choose an input between 20 MB and 2 GB for this example."
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
exit
}
set http [new_CkHttp]
CkHttp_put_AuthToken $http [CkHttpCurl_getVar $curl "access_token"]
set response [new_CkHttpResponse]
set request [new_CkJsonObject]
CkJsonObject_UpdateString $request "folder_id" [CkHttpCurl_getVar $curl "folder_id"]
CkJsonObject_UpdateString $request "file_name" "large-document.zip"
CkJsonObject_UpdateInt $request "file_size" $fileSize
set success [CkHttp_HttpJson $http "POST" "https://upload.box.com/api/2.0/files/upload_sessions" $request "application/json" $response]
if {$success != 1} then {
puts [CkHttp_lastErrorText $http]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
exit
}
if {[CkHttpResponse_get_StatusCode $response] != 201} then {
puts [CkHttpResponse_bodyStr $response]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
exit
}
set session [new_CkJsonObject]
set success [CkJsonObject_Load $session [CkHttpResponse_bodyStr $response]]
if {$success != 1} then {
puts [CkJsonObject_lastErrorText $session]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
exit
}
set success [CkJsonObject_WriteFile $session "qa_output/box_upload_session.json"]
if {$success != 1} then {
puts [CkJsonObject_lastErrorText $session]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
exit
}
set partSize [CkJsonObject_IntOf $session "part_size"]
set uploadUrl [CkJsonObject_stringOf $session "session_endpoints.upload_part"]
set commitUrl [CkJsonObject_stringOf $session "session_endpoints.commit"]
if {expr [$partSize <= 0] || [expr [$uploadUrl == ""] || [$commitUrl == ""]]} then {
puts "Invalid session response."
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
exit
}
set hash [new_CkCrypt2]
CkCrypt2_put_HashAlgorithm $hash "sha1"
CkCrypt2_put_EncodingMode $hash "base64"
set fileDigest [CkCrypt2_hashFileENC $hash $localPath]
if {[CkCrypt2_get_LastMethodSuccess $hash] != 1} then {
puts [CkCrypt2_lastErrorText $hash]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
exit
}
set success [CkFileAccess_FileOpen $file $localPath 2147483648 1 3 0]
if {$success != 1} then {
puts [CkFileAccess_lastErrorText $file]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
exit
}
set commit [new_CkJsonObject]
set partResponse [new_CkJsonObject]
set chunk [new_CkBinData]
set range [new_CkStringBuilder]
set digestHeader [new_CkStringBuilder]
set offset 0
set partIndex 0
while {$offset < $fileSize} {
CkBinData_Clear $chunk
set success [CkFileAccess_FileReadBd $file $partSize $chunk]
if {$success != 1} then {
puts [CkFileAccess_lastErrorText $file]
CkFileAccess_FileClose $file
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
exit
}
set partLength [CkBinData_get_NumBytes $chunk]
if {$partLength <= 0} then {
puts "Unexpected end of file."
CkFileAccess_FileClose $file
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
exit
}
set lastByte [expr $offset + [expr $partLength - 1]]
CkStringBuilder_Clear $range
CkStringBuilder_Append $range "bytes "
CkStringBuilder_AppendInt $range $offset
CkStringBuilder_Append $range "-"
CkStringBuilder_AppendInt $range $lastByte
CkStringBuilder_Append $range "/"
CkStringBuilder_AppendInt $range $fileSize
set partDigest [CkCrypt2_hashBdENC $hash $chunk]
if {[CkCrypt2_get_LastMethodSuccess $hash] != 1} then {
puts [CkCrypt2_lastErrorText $hash]
CkFileAccess_FileClose $file
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
exit
}
CkStringBuilder_SetString $digestHeader "sha="
CkStringBuilder_Append $digestHeader $partDigest
CkHttp_SetRequestHeader $http "Digest" [CkStringBuilder_getAsString $digestHeader]
CkHttp_SetRequestHeader $http "Content-Range" [CkStringBuilder_getAsString $range]
set success [CkHttp_HttpBd $http "PUT" $uploadUrl $chunk "application/octet-stream" $response]
if {expr [$success != 1] || [[CkHttpResponse_get_StatusCode $response] != 200]} then {
puts "Part failed. HTTP status: [CkHttpResponse_get_StatusCode $response]"
puts [CkHttpResponse_bodyStr $response]
puts "Inspect the saved session and server list_parts before retrying this part."
CkFileAccess_FileClose $file
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
exit
}
set success [CkJsonObject_Load $partResponse [CkHttpResponse_bodyStr $response]]
if {$success != 1} then {
CkFileAccess_FileClose $file
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
exit
}
CkJsonObject_put_I $commit $partIndex
# Copy the server's acknowledged part fields into the commit manifest.
CkJsonObject_UpdateString $commit "parts[i].part_id" [CkJsonObject_stringOf $partResponse "part.part_id"]
CkJsonObject_UpdateInt $commit "parts[i].offset" [CkJsonObject_IntOf $partResponse "part.offset"]
CkJsonObject_UpdateInt $commit "parts[i].size" [CkJsonObject_IntOf $partResponse "part.size"]
CkJsonObject_UpdateString $commit "parts[i].sha1" [CkJsonObject_stringOf $partResponse "part.sha1"]
set success [CkJsonObject_WriteFile $commit "qa_output/box_upload_parts.json"]
if {$success != 1} then {
puts [CkJsonObject_lastErrorText $commit]
CkFileAccess_FileClose $file
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
exit
}
set offset [expr $offset + $partLength]
set partIndex [expr $partIndex + 1]
}
CkFileAccess_FileClose $file
CkHttp_RemoveRequestHeader $http "Content-Range"
CkStringBuilder_SetString $digestHeader "sha="
CkStringBuilder_Append $digestHeader $fileDigest
CkHttp_SetRequestHeader $http "Digest" [CkStringBuilder_getAsString $digestHeader]
set attempts 0
set retryHeader [new_CkStringBuilder]
set retryMatch [new_CkJsonObject]
set completed 0
while {expr [$attempts < 10] && [$completed == 0]} {
set success [CkHttp_HttpJson $http "POST" $commitUrl $commit "application/json" $response]
if {$success != 1} then {
puts "Commit failed: [CkHttpResponse_get_StatusCode $response]"
puts [CkHttpResponse_bodyStr $response]
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
delete_CkStringBuilder $retryHeader
delete_CkJsonObject $retryMatch
exit
}
if {[CkHttpResponse_get_StatusCode $response] == 201} then {
set completed 1
} else {
if {[CkHttpResponse_get_StatusCode $response] != 202} then {
puts "Unexpected commit status: [CkHttpResponse_get_StatusCode $response]"
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
delete_CkStringBuilder $retryHeader
delete_CkJsonObject $retryMatch
exit
}
# Box's asynchronous commit response supplies Retry-After in seconds.
CkStringBuilder_SetString $retryHeader [CkHttpResponse_getHeaderField $response "Retry-After"]
if {[CkStringBuilder_RegexMatch $retryHeader "^[0-9]{1,2}$" $retryMatch 100] != 1} then {
puts "Commit pending. Preserve the session and inspect Retry-After before resuming: [CkStringBuilder_getAsString $retryHeader]"
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
delete_CkStringBuilder $retryHeader
delete_CkJsonObject $retryMatch
exit
}
set waitSeconds [CkStringBuilder_getAsString $retryHeader]
if {expr [$waitSeconds < 1] || [$waitSeconds > 60]} then {
puts "Commit pending. Resume commit after Retry-After: [CkHttpResponse_getHeaderField $response Retry-After]"
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
delete_CkStringBuilder $retryHeader
delete_CkJsonObject $retryMatch
exit
}
CkHttp_SleepMs $http [expr $waitSeconds * 1000]
}
set attempts [expr $attempts + 1]
}
if {$completed == 0} then {
puts "Commit still pending; retain session and part manifests to resume commit later."
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
delete_CkStringBuilder $retryHeader
delete_CkJsonObject $retryMatch
exit
}
puts "Upload committed: [CkHttpResponse_bodyStr $response]"
delete_CkHttpCurl $curl
delete_CkStringBuilder $setupCommand
delete_CkStringBuilder $authorizedCommand1
delete_CkFileAccess $file
delete_CkStringBuilder $sizeText
delete_CkJsonObject $sizeMatch
delete_CkHttp $http
delete_CkHttpResponse $response
delete_CkJsonObject $request
delete_CkJsonObject $session
delete_CkCrypt2 $hash
delete_CkJsonObject $commit
delete_CkJsonObject $partResponse
delete_CkBinData $chunk
delete_CkStringBuilder $range
delete_CkStringBuilder $digestHeader
delete_CkStringBuilder $retryHeader
delete_CkJsonObject $retryMatch