Home > Enterprise >  Sending a file via multipart using libcurl in C
Sending a file via multipart using libcurl in C

Time:09-22

I am trying to upload a file via a POST request to a remote location using libcurl and C . However, I think I am doing something wrong because I am told that the file doesn't arrive on the other side.

I am using the following code:

#include "curl/curl.h"

using namespace std;

size_t WriteCallback(void * buffer, size_t size, size_t count, void * user)
{
  ((string *) user)->append((char *) buffer, 0, size * count);
  return size * count;
}

int main()
{

  curl_global_init(CURL_GLOBAL_ALL);

  CURL * Curl;
  CURLCode res;
  Curl = curl_easy_init();
  curl_easy_setopt(Curl, CURLOPT_FAILONERROR, 0);
  curl_easy_setopt(Curl, CURLOPT_VERBOSE, 1L);

  struct curl_httppost * formpost = NULL;
  struct curl_httppost * lastptr = NULL;
  struct curl_slist * headerlist = NULL;
  static const char buf[] = "Expect:";

  ImageName = "myimage.jpg"
  ImageNameWithPath = "/this/location/right/here/"   ImageName;

  curl_formadd(& formpost,
               & lastptr,
               CURLFORM_COPYNAME, ImageName.c_str();
               CURLFORM_FILE, ImageNameWithPath.c_str();
               CURLFORM_CONTENTTYPE, "image/jpeg (binary)";
               CURLFORM_END);

  curl_formadd(& formpost,
               & lastptr,
               CURLFORM_COPYNAME, ImageName.c_str();
               CURLFORM_COPYCONTENTS, ImageNameWithPath.c_str();
               CURLFORM_CONTENTTYPE, "image/jpeg (binary)";
               CURLFORM_END);

  headerlist = curl_slist_append(headerlist, buf);

  string MessageBodyLine1 = "Content-Disposition: form-data; name=\"file\"; filename\""   ImageName   "\"";
  headerlist = curl_slist_append(headerlist, MessageBodyLine1.c_str());

  string Url = https://www.example.com/ // There is a real URL here
  curl_easy_setopt(Curl, CURLOPT_URL, Ulr.c_str());

  curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, headerlist);
  curl_easy_setopt(Curl, CURLOPT_HTTPPOST, formpost);

  string Reponse;
  curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, WriteCallback);
  curl_easy_setopt(Curl, CURLOPT_WRITEDATA, & Response);


  res = curl_easy_perform(Curl);
  curl_easy_cleanup(Curl);
  curl_formfree(formpost);
  curl_slist_free_all(headerlist);

  curl_global_cleanup();

  return 0;

I actually expected the output to look something like this somewhere

POST URL HTTP/1.1
Content-Type: multipart/form-data; boundary=----BoundaryString

----BoundaryString
Content-Disposition: form-data; name="file"; filename="myfile.jpg"
Content-Type: image/jpeg (binary)

and instead I get this:

* Trying IP
* Connected to IP port PORT (#0)
POST URL HTTP/1.1
Host: IP
Accept: IP:PORT
Content-Disposition: form-data; name="file"; filename="myfile.jpg"
Content-Length: totalsize

Content-Type: multipart/form-data; boundary=----BoundaryString

< HTTP/1.1 200 OK
[...]

Now it says OK but I know for a fact that the image does not arrive on the other side. Furthermore my Response string is also empty whereas it should contain a JSON string.

What am I doing wrong? Unfortunately I am stuck with a older libcurl version and thus cannot use the mime format available in curl 7.55.

CodePudding user response:

DO NOT add a Content-Disposition request header to the headerlist of the HTTP request, it does not belong there. It belongs inside each MIME part instead (ie, you should be using CURLFORM_COPYNAME, "file" and CURLFORM_FILE, ImageName.c_str()). I would expect curl_formadd() to handle the Content-Disposition for you, you should not need to create it manually.

Also, image/jpeg (binary) is not a valid Content-Type value, it needs to be just image/jpeg.

And why are you calling curl_formadd() twice for the same file, one with CURLFORM_FILE and the other with CURLFORM_COPYCONTENTS? Especially since your input to CURLFORM_COPYCONTENTS is wrong (it expects a pointer to actual data, not a pointer to a filename string). You should be using only CURLFORM_FILE in this situation.

Try this:

#include "curl/curl.h"
#include <string>
using namespace std;

size_t WriteCallback(void * buffer, size_t size, size_t count, void * user)
{
  size_t numBytes = size * count;
  static_cast<string*>(user)->append(static_cast<char*>(buffer), 0, numBytes);
  return numBytes;
}

int main()
{
  curl_global_init(CURL_GLOBAL_ALL);

  CURL *Curl = curl_easy_init();
  if (!Curl)
    return -1;

  curl_easy_setopt(Curl, CURLOPT_FAILONERROR, 0);
  curl_easy_setopt(Curl, CURLOPT_VERBOSE, 1L);

  curl_httppost *formpost = NULL;
  curl_httppost *lastptr = NULL;

  string ImageName = "myimage.jpg";
  string ImageNameWithPath = "/this/location/right/here/"   ImageName;

  curl_formadd(&formpost,
               &lastptr,
               CURLFORM_COPYNAME, "file",
               CURLFORM_FILE, ImageNameWithPath.c_str(),
               CURLFORM_CONTENTTYPE, "image/jpeg",
               CURLFORM_END);

  curl_slist *headerlist = curl_slist_append(NULL, "Expect:");

  string Url = https://www.example.com/ // There is a real URL here
  curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());

  curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, headerlist);
  curl_easy_setopt(Curl, CURLOPT_HTTPPOST, formpost);

  string Reponse;
  curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, WriteCallback);
  curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &Response);

  CURLCode res = curl_easy_perform(Curl);

  curl_easy_cleanup(Curl);
  curl_formfree(formpost);
  curl_slist_free_all(headerlist);

  curl_global_cleanup();

  return 0;
}
  • Related