Uploads over HTTP can be done in many different ways and it is important to notice the differences. They can use different methods, like POST or PUT, and when using POST the body formatting can differ.
In addition to those HTTP differences, libcurl offers different ways to provide the data to upload.
POST is typically the HTTP method to pass data to a remote web application. A common way to do that in browsers is by filling in a HTML form and pressing submit. It is the standard way for a HTTP request to pass on data to the server. With libcurl you normally provide that data as a pointer and a length:
curl_easy_setopt(easy, CURLOPT_POSTFIELDS, dataptr);
curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, (long)datalength);
Or you tell libcurl that it is a post but would prefer to have libcurl instead get the data by using the regular read callback:
curl_easy_setopt(easy, CURLOPT_POST, 1L);
curl_easy_setopt(easy, CURLOPT_READFUNCTION, read_callback);
This "normal" POST will also set the request header Content-Type: application/x-www-form-urlencoded
.
A multipart formpost is still using the same HTTP method POST; the difference is only in the formatting of the request body. A multipart formpost is basically a series of separate "parts", separated by MIME-style boundary strings. There's no limit to how many parts you can send.
Each such part has a name, a set of headers and a few other properties.
libcurl offers a convenience function for constructing such a series of parts
and to send that off to the server. curl_formadd
is the function to build a
formpost. Invoke it once for each part, and pass in arguments to it
detailing the specifics and characteristics of that part. When all parts you
want to send have been added, you pass in the handle curl_formadd
returned
like this:
curl_easy_setopt(easy, CURLOPT_HTTPPOST, formposthandle);
A PUT with libcurl will assume you pass the data to it using the read
callback, as that is the typical "file upload" pattern libcurl uses and
provides. You set the callback, you ask for PUT (by asking for
CURLOPT_UPLOAD
), you set the size of the upload and you set the URL to the
destination:
curl_easy_setopt(easy, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(easy, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
curl_easy_setopt(easy, CURLOPT_READFUNCTION, read_callback);
curl_easy_setopt(easy, CURLOPT_URL, "https://example.com/handle/put");
If you for some reason do not know the size of the upload before the transfer
starts, and you are using HTTP 1.1 you can add a Transfer-Encoding: chunked
header with CURLOPT_HTTPHEADER. For HTTP 1.0 you
must provide the size before hand and for HTTP 2 and later, neither the size
nor the extra header is needed.
When doing HTTP uploads using HTTP 1.1, libcurl will insert an Expect: 100-continue
header in some circumstances. This header offers the server a
way to reject the transfer early and save the client from having to send a lot
of data in vain before the server gets a chance to decline.
The header is added by libcurl if HTTP uploading is done with CURLOPT_UPLOAD
or if it is asked to do a HTTP POST for which the body size is either unknown
or known to be larger than 1024 bytes.
A libcurl-using client can explicitly disable the use of the Expect:
header
with the CURLOPT_HTTPHEADER option.