0

The CURL command that I am using is shown below.

curl -F 'file=@/home/karthik/Workspace/downloadfile.out' http://127.0.0.1:5000/file-upload --verbose

The response from the server is shown below.

*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
> POST /file-upload HTTP/1.1
> Host: 127.0.0.1:5000
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 663876790
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------4e96ef0714498bd7
> 
< HTTP/1.1 100 Continue
* HTTP 1.0, assume close after body
< HTTP/1.0 201 CREATED
< Content-Type: application/json
< Content-Length: 46
< Server: Werkzeug/0.16.0 Python/3.5.2
< Date: Sat, 14 Dec 2019 07:05:15 GMT
< 
{
  "message": "File successfully uploaded"
}
* Closing connection 0

I want to implement the same command in C/C++ using libcurl. I am using the following function.

int FileUploadDownload::upload(const std::string &filename, const std::string &url) {

    CURL *curl;
    CURLcode res;
    struct stat file_info;
    curl_off_t speed_upload, total_time;
    FILE *fd;

    fd = fopen(filename.c_str(), "rb");
    if(!fd) {
        m_logger->errorf("unable to open file: %s\n",strerror(errno));
        return 1;
    }
    if(fstat(fileno(fd), &file_info) != 0) {
        m_logger->errorf("unable to get file stats: %s\n",strerror(errno));
        return 2;
    }

    std::cout << "filename : "<< filename << std::endl;
    std::cout << "url : " << url << std::endl;

    curl = curl_easy_init();
    if(curl) {

        curl_easy_setopt(curl, CURLOPT_URL,
                         url.c_str());

        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, filename.c_str());
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        curl_easy_setopt(curl, CURLOPT_READDATA, fd);
        curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,
                         (curl_off_t) file_info.st_size);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

        res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_perform() failed: %s\n",curl_easy_strerror(res));
        } else {
            curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &speed_upload);
            curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time);
            m_logger->infof("Speed: %" CURL_FORMAT_CURL_OFF_T " bytes/sec during %"
                            CURL_FORMAT_CURL_OFF_T ".%06ld seconds\n",
                            speed_upload,
                            (total_time / 1000000), (long) (total_time % 1000000));
        }
    }
    return 0;
}

The below is the result that I get from the server.

The result that I get is shown below.
   *   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
> POST /file-upload HTTP/1.1
> Host: 127.0.0.1:5000
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 550
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------c8ef4837136fca99
> 
< HTTP/1.1 100 Continue
* HTTP 1.0, assume close after body
< HTTP/1.0 201 CREATED
< Content-Type: application/json
< Content-Length: 46
< Server: Werkzeug/0.16.0 Python/3.5.2
< Date: Sat, 14 Dec 2019 07:09:47 GMT
< 
{
  "message": "File successfully uploaded"
}
* Closing connection 0

My aim is to mimic the curl command above in the C/C++ code below. What am I doing wrong ?

I have some difficulty in update to the latest libcurl. However I tried to set the HTTPHEADERS manually as follows.

    stringstream ss;

    ss << "Content-Type:" << " multipart/form-data";
    list = curl_slist_append(list, ss.str().c_str());
    ss.str(std::string());

    ss << "filename=" << filename;
    list = curl_slist_append(list, ss.str().c_str());
    ss.str(std::string());

    ss << "name=file";
    list = curl_slist_append(list, ss.str().c_str());
    ss.str(std::string());

    ss << "Content-Type:" << " text/plain";
    list = curl_slist_append(list, ss.str().c_str());
    ss.str(std::string());

    ss << "Content-Disposition:" << " form-data";
    list = curl_slist_append(list, ss.str().c_str());

    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);

But the below code doesn't seem to work. Is the above approach valid ? If yes, what am I doing wrong?

1
  • The easiest way to get the exact code you need for libcurl is to run the curl command with --libcurl test.c. Then you will get the code you need. I use it all the time. Commented Jan 31, 2022 at 13:44

1 Answer 1

2

To upload file you need to send data with content type multipart/form-data.

For that you need to create form and attach file data in that form as below.

#include <curl/curl.h>

#include <errno.h>
#include <iostream>
#include <stdio.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <string.h>

int upload(const std::string &filename, const std::string &url) {

  CURL *curl;
  CURLcode res;
  struct stat file_info;
  curl_off_t speed_upload, total_time;
  FILE *fd;

  curl_mime *form = NULL;
  curl_mimepart *field = NULL;
  struct curl_slist *headerlist = NULL;
  static const char buf[] = "Expect:";

  fd = fopen(filename.c_str(), "rb");
  if (!fd) {
    printf("unable to open file: %s\n", strerror(errno));
    return 1;
  }
  if (fstat(fileno(fd), &file_info) != 0) {
    printf("unable to get file stats: %s\n", strerror(errno));
    return 2;
  }

  std::cout << "filename : " << filename << ":"
            << std::endl;
  std::cout << "url : " << url << std::endl;

  curl = curl_easy_init();
  if (curl) {
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
    form = curl_mime_init(curl);

    /* Fill in the file upload field */
    field = curl_mime_addpart(form);
    curl_mime_name(field, "file");
    curl_mime_filedata(field, filename.c_str());

    headerlist = curl_slist_append(headerlist, buf);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
    curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);

    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
      printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    } else {
      curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &speed_upload);
      curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time);
      printf("Speed: %" CURL_FORMAT_CURL_OFF_T
             " bytes/sec during %" CURL_FORMAT_CURL_OFF_T ".%06ld seconds\n",
             speed_upload, (total_time / 1000000),
             (long)(total_time % 1000000));
    }
  }
  return 0;
}

int main(int argc, char **argv) { return upload(argv[1], argv[2]); }

Note:

Usage: ./a.out FileName ServerURL

Sign up to request clarification or add additional context in comments.

4 Comments

It seems that curl_mime_init() is not present for me.
@liv2hak then you're using an old (older than 4 October 2017) version of libcurl. update. curl_mime_init() was added in the libcurl 7.56.0 release from october 2017.
@ManthanTilva - Hi I cannot update the version of libcurl due to some project constraints. I have updated the question with an alternative approach. Can you please comment on the last part?
For ancient libcurl versions, stick to curl_formadd as shown in the postit2-formadd example

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.