A great majority of the requests done through HTTP result in a response; there are two different ways of accessing the response depending on its size:
-
GetResponseDatafor small data (less than 20MB) and -
ReadDatafor anything larger
The GetResponseData is mostly used for
simple requests that don't return a lot of data. It's regularly used
for API requests that expect short responses, for example:
HTTPClient ino_client
'''' rem n_cst_azurefs.of_getcontainers:13
li_res = ino_client.Sendrequest( "GET", of_geturl(URL_LIST_CONTAINERS) )
li_responseStatus = ino_client.GetResponseStatuscode( )
if li_responseStatus <> 200 then
as_error = ino_client.GetResponseStatusText( )
return 1
end if
ino_client.GetResponseBody(ref lblb_responseBytes)
ls_response = string(lblb_responseBytes, EncodingUTF8!)The GetResponseData can read data onto
both a string or a blob, so it's perfect for small data.
However, this method does not support working with data larger than 20MB.
The ReadData method is used to read the
response in chunks, thus allowing the read of arbitrarily large
responses. This is the method used to download the files in the
sample application. Since the data to be read may be larger than
what a Blob object can hold, it's recommended that the chunks be
written to a file on disk as they're read.
Note: It is necessary to stop the HTTPClient object from automatically loading the data:
''' n_cst_filetransferhelper.of_download:25 lb_autoread = ino_client.Autoreaddata ino_client.Autoreaddata = false
Then we can send the request for the data:
li_res = ino_client.SendRequest( "GET", as_url)
Finally, we read and send to disk the received data, reading
chunk by chunk. The ReadData method
automatically reads the data following what was previously read. The
FileWriteEx too writes at the end of what was
previously written.
''' n_cst_filetransferhelper.of_download:29
do while true
lblb_buffer = Blob("")
li_ret = ino_client.readdata(ref lblb_buffer, CHUNK_SIZE)
FileWriteEx(al_file, lblb_buffer)
ll_chunksWritten++
...
loopDon't forget to restore the value of
AutoReadData:
ino_client.Autoreaddata = lb_autoread
Note: Reading and writing to disk by chunks might take a long time, so it's recommended to yield() between chunks to prevent the application from freezing.
For the Azure example, the Azure REST API currently only allows to upload files through PUT requests, and currently the HTTP Client doesn't have a supported way to upload files larger than 20MB with a single call. Thus, to upload our files we have to upload files in chunks, making use of Azure's Put Block and Put Block List endpoints to submit the files in parts.
To read the file in parts the FileReadEx in PowerScript Reference method is used.
''' n_cst_azurefs.of_uploadfile:28
ll_filesize = FileLength64(as_path)
li_file = FileOpen(as_path, StreamMode!, Read!, LockRead!)
if li_file < 1 then
as_error = "Could not open file for reading"
return -1
end if
li_chunk = 0
do while true
ll_read = FileReadEx(li_file, ref lblb_buffer, CHUNK_SIZE)
if ll_read < 1 then exit
lblb_pathHash = lno_crypter.SHA(SHA512!, Blob(as_path + string(li_chunk), EncodingUTF8!))
ls_blockId = lno_coderobject.base64encode(lblb_pathHash)
ls_blockId = Left(ls_blockId, 64)
li_res = of_putblock(&
lblb_buffer,&
ls_sanitizedObjectPath,&
lno_coderobject.urlencode(Blob(ls_blockId, EncodingUTF8!)),&
ref ls_error)Each chunk of the file will have an ID generated (this is needed to commit all the pieces into a single file on Azure) and immediately after, the chunk will be uploaded.
To upload a chunk of the file, all that's necessary is to send a PUT request to the URL appending the Blob as an argument to the SendRequest method:
''' n_cst_filetransferhelper.of_uploadPut:7
int li_res
int li_responseCode
string ls_responseText
ino_client.SetRequestheader( "Content-Length", String(Len(ablb_blob)), true)
li_res = ino_client.SendRequest("PUT", as_url, ablb_blob)
if li_res <> 1 then
as_error = "couldn't send request"
return -1
end if
li_responseCode = ino_client.GetResponseStatuscode( )
ls_responseText = ino_client.GetResponseStatusText( )
if li_responseCode < 200 or li_responseCode > 300 then
as_error = "Received non-success status code " + string(li_responseCode) + ": " + ls_responseText
return -1
end if
return 1Once all the chunks have been uploaded and the IDs collected, we have to send another request indicating the instruction to compile all the chunks into a single file. The list of chunks is built as an XML document, thus we have to indicate such in the request header (with SetRequestHeader).
''' n_cst_azurefs.of_putblocklist:8
ls_xml = "<?xml version='1.0' encoding='utf-8'?><BlockList>"
for i = 1 to UpperBound(as_blockids)
ls_xml += "<Uncommitted>" + as_blockids[i] + "</Uncommitted>"
next
ls_xml += "</BlockList>"
of_prepareclient( "PUT", len(ls_xml), "/" + is_accname + "/" + is_container + "/" + as_destination, "comp:blocklist", "application/xml")
ino_client.SetRequestheader( "Content-Type", "application/xml", true)
li_res = ino_client.SendRequest("PUT", &
ls_url,&
ls_xml)Finally we send the PUT request to the URL and pass the XML as the body of the request.
The procedure above is used to work with Azure's particular constraints; however a more general approach is to upload a file in chunks using a POST method. We use this other approach with the C# file server.
/// n_cst_genericfileserverbrowser.of_uploadfile:37
li_res = ino_client.postdatastart(ls_url) // initiate the chunked requeset
if li_res <> 1 then
as_error = "Error attempting file transfer"
return -1
end if
do while lll_transferred < ll_fileSize
lblb_buffer = Blob("")
li_res = FileReadEx(li_handle, lblb_buffer, CHUNK_SIZE) // read a chunk of the data
if li_res = 0 then exit
if li_res < 1 then
as_error = "Error reading file chunk"
return -1
end if
ino_client.postdata( lblb_buffer, li_res) // send the chunk of the data
lll_transferred += li_res
loop // repeat until all the file has been read
li_res = ino_client.postdataend() This is the way to send very large files to an endpoint expecting a POST action, there's currently not an equivalent operation for the PUT method.
The demo application contains an NVO with the logic required to download and upload files through HTTP called n_cst_filetransferhelper. This tool will help you work with downloading/uploading files by taking care of the nitty-gritty and providing a single method for each operation. Feel free to download it and use it in your own projects and/or modify it to better accommodate your requirements.


