libcURL POST 分段上传(带有缓冲图像)返回 HTTP 400
libcURL POST multipart upload (with buffered image) returning HTTP 400
我正在向Cloudinary API发送一个POST多部分,以上传我使用openCV捕获和缓冲的图像。Cloudinary给了我一个HTTP 400。我已经测试过使用 chrome 应用程序上传图像PostMan
并且有效,因此我的 POST 请求中一定有错误或缓冲数据的方式有问题。
以下是他们描述对 API 的调用的方式:http://cloudinary.com/documentation/upload_images#uploading_with_a_direct_call_to_the_api
我已经在这里待了好几天了,任何帮助将不胜感激。一旦弄清楚了这一点,我将把这段代码提交给Cloudinary,这样其他摆弄C/C++的用户可以轻松地上传到他们的平台,而不会像我所经历的那样痛苦。
//Clone frame and encode into buffer as JPEG
cv::Mat hand_roi = frame.clone();
std::vector<unsigned char> imgBuffer;
imencode(".jpg", hand_roi, imgBuffer);
//Buffer to String with pointer/length
std::string imgStrBuff = std::string(imgBuffer.begin(), imgBuffer.end());
char *imgBuffPtr = (char *)&imgStrBuff;
long imgBuffLength = static_cast<long>(imgStrBuff.size());
//Unix time in (s)
std::time_t t = std::time(0);
std::string unixTime = std::to_string(t);
//API_Secret:
std::string APISecret = "XXXXXXXXXXXXXXXXXXXXX";
//Create SHA1 signature required by cloudinary
std::string hashString = "timestamp=" + unixTime + APISecret;
std::string authSig = sha1(hashString);
//POST URL
std::string url = "https://api.cloudinary.com/v1_1/xxxxxxxxxxx/image/upload";
/*-----CURL SETUP-----*/
//Set headers as null
struct curl_slist *headers = NULL;
struct curl_httppost *formpost = NULL;
struct curl_httppost *lastptr = NULL;
//Data type
curl_slist_append(headers, "Content-Type: multipart/form-data");
curl_global_init(CURL_GLOBAL_ALL);
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "api_key",
CURLFORM_COPYCONTENTS, "xxxAPIKeyxxx",
CURLFORM_END);
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "timestamp",
CURLFORM_COPYCONTENTS, unixT.c_str(),
CURLFORM_END);
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "signature",
CURLFORM_COPYCONTENTS, authSig.c_str(),
CURLFORM_END);
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "file",
CURLFORM_BUFFER, "random_img_name.jpg",
CURLFORM_BUFFERPTR, imgBuffPtr,
CURLFORM_BUFFERLENGTH, imgBuffLength,
CURLFORM_END);
//init easy_curl
CURL* curl = curl_easy_init();
if(!curl)
return false;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
res = curl_easy_perform(curl);
我基本上已经阅读了所有的libcurl文档,但我只是无法弄清楚为什么这些请求被拒绝。
您的代码有几个问题,我将逐一解决它们,从导致操作实际失败的最关键问题开始。
1(您正在向CURL提供垃圾指针作为文件缓冲区
问题出在这一部分:
std::string imgStrBuff = std::string(imgBuffer.begin(), imgBuffer.end());
char *imgBuffPtr = (char *)&imgStrBuff;
long imgBuffLength = static_cast<long>(imgStrBuff.size());
这样做:它将imgBuffer
填充到C++字符串imgStrBuff
中,然后将指向字符串描述符的指针误解为指向实际数据的指针,并将其存储在imgBuffPtr
中。数据的(正确(长度存储在 imgBuffLength
中。
因此,再往下,您要求 CURL 从地址 imgBuffPtr
开始从内存中读取 imgBuffLength
个字节......而且你很幸运,你的程序不只是崩溃,因为它现在将读取字符串描述符和任何垃圾,而不是实际数据!
我想你真正想做的是char *imgBuffPtr = imgStrBuff.c_str();
,但有一个更好的方法,中间没有任何字符串:
char *imgBuffPtr = &imgBuffer[0];
long imgBuffLength = imgBuffer.size();
该标准要求std::vector
将其数据存储在连续内存中,因此将其保存为仅使用指向其第一个元素的指针。
2(您没有在正确的时间打电话给curl_global_init
curl_global_init
应在任何其他 CURL 函数之前调用,但只需调用一次。因此,在此特定代码中,它必须高于对curl_slist_append
的调用,但实际上您只需要在程序的启动例程中的某个地方使用它。
3( 您没有将curl_slist_append
的结果分配给headers
此行实际上什么都不做(除了创建列表并泄漏它(:
curl_slist_append(headers, "Content-Type: multipart/form-data");
这是因为您必须将其返回值赋回变量 headers
,如下所示:
headers = curl_slist_append(headers, "Content-Type: multipart/form-data");
4( Content-Type
标题是不必要的
正如评论者Daniel Stenberg已经指出的那样,没有必要将内容类型设置为标题,因为CURL_HTTPPOST
已经为您执行此操作 - 实际上,它必须这样做,因为它还必须定义多部分边界。
5(您正在泄漏数据结构
以下内容仅适用于代码中没有此功能的情况:
您正在创建一个简单的会话变量curl
,一个curl_slist
headers
(尽管#4已过时(和curl_httppost
结构formpost
。
完成后,必须使用以下代码再次释放它们:
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headers);
为将来提供一些调试帮助
下次遇到此类问题时,您应该明确执行两件事:
正如评论者Remy Lebeau所建议的那样,使用数据包嗅探器(如Wireshark或SmartSniff(或分析代理(如Fiddler(来查看实际发送或接收的内容。您可能需要将协议更改为
http://
才能正常工作(之后将其更改回来!(,否则您将只能看到加密的SSL数据。通过这种方式,您可以了解真正越界的情况以及问题可能出在哪里。然后,您还可以比较数据的两个版本,一个有效(例如从命令行或浏览器(,另一个版本不起作用。检查从 API 返回的实际响应正文。您可能需要
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 0);
以便即使状态代码为>=400,您也会首先获得一个正文,以及一个数据写入回调(查看此示例(。如果您这样做,您会注意到 API 发送的错误是{"error":{"message":"Invalid image file"}}
这已经为您提供了线索。
兄弟,你把一切都搞砸了。下面是示例代码,请你重构代码。最大的问题是curl_global_init
它必须是第一个,然后curl_slist_append
应该接受新的指针。
以下面的示例为例,开始逐个更新代码,并且应该全部设置
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
int main(int argc, char *argv[])
{
CURL *curl;
CURLcode res;
struct curl_httppost *formpost=NULL;
struct curl_httppost *lastptr=NULL;
struct curl_slist *headerlist=NULL;
static const char buf[] = "Expect:";
curl_global_init(CURL_GLOBAL_ALL);
/* Fill in the file upload field */
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "sendfile",
CURLFORM_FILE, "postit2.c",
CURLFORM_END);
/* Fill in the filename field */
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "filename",
CURLFORM_COPYCONTENTS, "postit2.c",
CURLFORM_END);
/* Fill in the submit field too, even if this is rarely needed */
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "submit",
CURLFORM_COPYCONTENTS, "send",
CURLFORM_END);
curl = curl_easy_init();
/* initalize custom header list (stating that Expect: 100-continue is not
wanted */
headerlist = curl_slist_append(headerlist, buf);
if(curl) {
/* what URL that receives this POST */
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/examplepost.cgi");
if ( (argc == 2) && (!strcmp(argv[1], "noexpectheader")) )
/* only disable 100-continue header if explicitly requested */
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %sn",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
/* then cleanup the formpost chain */
curl_formfree(formpost);
/* free slist */
curl_slist_free_all (headerlist);
}
return 0;
}
- C++,OpenCV,尝试显示图像时"OpenCV(4.3.0) Error: Assertion failed (size.width>0 && size.height>0)"此错误
- 如何使用OpenCV将RBG图像转换为HSV,并将H、S和V值保存为C++中的3个独立图像
- OpenCV EqualizeHist()从彩色图像创建黑白图像
- 将"打开的CV图像"中的"颜色"转换为整数格式
- 平均图像时图像损坏
- 在C++中使用GDAL可以将图像的像素坐标转换为lat,long吗
- 如何将图像传输到c++(dll)中的缓冲区,然后在c#的缓冲区中读/写
- Vulkan验证层不断在VkQueuePresentKHR()上抛出图像布局错误
- 使用FFMPEG将RGB图像序列保存到.mp4时出现问题
- 将RGB图像保存为PPM格式
- 将图像添加到资源文件夹UWP C++
- 有没有办法避免在OpenCV中使用RTSP相机时缓冲图像?
- 使用 GDI+ 对图像进行双重缓冲
- libcURL POST 分段上传(带有缓冲图像)返回 HTTP 400
- 分层图像作为具有大量层的帧缓冲
- 将一系列图像写入缓冲存储器的最佳方式
- 观看3D图像与AMD Quad缓冲SDK - DirectX 9
- WxWidgets中的缓冲图像
- 基于FFMpeg的BGR缓冲图像到YUV的快速转换
- 三维头部的透视投影和z -缓冲以形成面部图像