在 C++ 中使用 libcurl 通过多部分发送文件

Sending a file via multipart using libcurl in C++

我正在尝试使用 libcurl 和 C++ 通过 POST 请求将文件上传到远程位置。但是,我认为我做错了什么,因为我被告知文件没有到达另一端。

我正在使用以下代码:

#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;

我实际上希望某处的输出看起来像这样

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)

相反,我得到了这个:

* 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
[...]

现在它说好的,但我知道图像没有到达另一边。此外,我的 Response 字符串也是空的,而它应该包含一个 JSON 字符串。

我做错了什么?不幸的是,我坚持使用较旧的 libcurl 版本,因此无法使用 curl 7.55 中可用的 mime 格式。

不要在 HTTP 请求的 headerlist 中添加 Content-Disposition 请求 header,它不属于那里。它属于每个 MIME 部分(即,您应该使用 CURLFORM_COPYNAME, "file"CURLFORM_FILE, ImageName.c_str())。我希望 curl_formadd() 为您处理 Content-Disposition,您不需要手动创建它。

此外,image/jpeg (binary) 不是有效的 Content-Type 值,它只需要 image/jpeg.

你为什么要为同一个文件两次调用 curl_formadd(),一次调用 CURLFORM_FILE,另一次调用 CURLFORM_COPYCONTENTS?特别是因为您对 CURLFORM_COPYCONTENTS 的输入是错误的(它需要指向实际数据的指针,而不是指向文件名字符串的指针)。在这种情况下,您应该只使用 CURLFORM_FILE

试试这个:

#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;
}