http::POST is an extension of the standard http::geturl commands, providing an easy way to gather all the POST parameters - including files to be uploaded - and to set them up in a proper multipart data-structure that will be passed to the underlying http::geturl command.
Version 1.0.1 (May 2019)
http::POST 1.0.1 http POST with multipart/form-data
extension of http::geturl for posting data and files
package require Tcl 8.5
package require http::POST ?1.0.1?
The http::POST package provides a command for sending HTTP POST requests with data and (very large) files.
http::POST takes care to gather all the parameters and files to transmit, in a single, large, virtual, mime-multipart channel. Note that all the assembled parts (including very large files such as images, videos, ...) are NOT assembled in memory, nor in a temporary file; they are assembled in a virtual stream and transmitted one block at a time. The return value of http::POST is a token for the transaction, just the same token returned by the underlying http::geturl command.
http::POST accepts all the valid http::geturl's parameters/options, plus a special option : -form
fieldName fieldValue Be aware that if you specify contents through channels (eg. using the fieldType -channel), the http::POST command will take ownership of these channels, and they will be closed at the end of the upload-phase. You should take care of closing these channels only if http::POST fails before starting the upload (e.g. bad-url or network errors..)
Let's suppose we should send some data to service http://somesite/someService by filling thes following form field name, customerId,... passportPhoto (upload of an picture), videocap.. (upload of a short video..))
# prepare a form-specification .... set myformSpec { { "surname" -text "Barrow Smith" } { "name" "John" } { "customerID" -text "123456" } { "attachment1" -file "/data/document1.pdf" } { "passportPhoto" -file "/data/mypic.jpg" } { "privacyCheck" -text "yes"} } # then add a last more complex parameter: # in the "videocap" parameter we pass a stream channel.. (eg $myChannel1 ) # Note that we should also set the mimeType (since it cannot be inferred from the channelName) # lappend myFormSpec [list "videocap" -channel $myChannel1 video/mp4 "filename=Cap1234567.mp4"] set token [http::POST https://xyz.com/registerme -form $myformSpec] # get the response from token ... # (usually it's a json-formatted response or an XML-formatted response ... it's your resoponsability to decode it ...) # ... # ... myDecode [http::data $token] # ... http:cleanup $token # note that the ownership of myChannel1 has been ''taken' by http::POST. # Now that the transmissin is ended, myChannel1 is closed
http::POST requires some auxiliary standard packages:
http::POST
Networking
Copyright (c) 2019 A.Buratti
The following is complete example of how to pass data to a real service. We will use an upload service offered by https://platerecognizer.com allowing to send a picture of a car-plate and then get the plate-id as a string. This service requires an authorization token (APIKEY) that can be obtained for free; see http://docs.platerecognizer.com
package require http::POST package require tls 1.7 http::register https 443 ::tls::socket set APIKEY 4c33e60562f57aeb04b09978267c412a9878707f ;# write here your APIKEY set myCarPlateImg ./carplate1.jpg ;# .. # I used a test plate downloaded from # https://images.summitmedia-digital.com/topgear/images/2018/07/19/license-plate-main.jpg set URL https://api.platerecognizer.com/v1/plate-reader set headers [list Authorization [list Token $APIKEY]] set form {} lappend form [list upload -file $myCarPlateImg] http::POST $URL -headers $headers -form $form set data [http::data $token] http::cleanup $token # result is in $data. It should be parsed ( use package json ) # {"processing_time":281.635,"version":1,"results":[{"box":{"xmin":171,"ymin":123,"ymax":283,"xmax":395},"plate":"r08097","score":0.769,"dscore":0.698}],"filename":"17_08_carplate1.jpg"} # Note a small error ... plate should be R0B097 (insted of r09097)
For another example, we are going to use a different service: https://vgy.me
This site provides a photo-upload service wiithout requiring a registration, therefore no authorization token. Playing with this service, you could post both a photo and some text.
The most interesting aspect of this service is related to the text we're going to transmit; in general all the API services require text as utf-8 strings. If you just write in English, you could simply ignore all this stuff but, how can you transmit some a greek text like this "Καφές: 1,20 ευρώ" (Coffe: 1,20 euro) ?
Hereis the solution
package require http::POST package require tls 1.7 # note: this target server require servername during the https handshake http::register https 443 {::tls::socket -autoservername true} set URL https://vgy.me set form {} lappend form [list "file" -file "./coffee.jpg" image/jpeg] lappend form [list title [encoding convertto utf-8 "Καφές"]] lappend form [list description [encoding convertto utf-8 "Καφές: 1,20 ευρώ"]] set token [http::POST ${URL}/upload -form $form] set data [http::data $token] http::cleanup $token # result is in $data. It should be parsed ( use package json ) #{"error":false,"size":151424,"filename":"oiy6Kg","ext":"jpg","url":"https://vgy.me/u/oiy6Kg","image":"https://i.vgy.me/oiy6Kg.jpg","delete":"https://vgy.me/delete/Cuf2MrZAJ4wU"} # .. please don't abuse of this service; if you just uploaded some images for test, remove them by calling the "delete" URL you found in the response.