ESP32(IDF4.4)HTTP POST上传JPEG至服务器失败求助
问题与解决
我用搭载IDF 4.4的ESP32,想通过HTTP POST把相机读取的JPEG字节数组上传到PHP服务器,服务器仅需存储图片。PC端Python脚本测试正常,服务器$_FILES能拿到数据;但ESP32上传时无报错,服务器的$_FILES和$_POST却全为空。不清楚怎么在ESP32的POST请求里加Python和PHP脚本中用于识别文件的"image"键,同时想了解除了查看超全局变量外,还有哪些调试服务器接收内容的方法。
核心问题原因
Python的requests.post(url, files=file)会自动使用multipart/form-data格式上传文件,这是PHP的$_FILES能识别的标准文件上传格式。但你的ESP32代码直接把JPEG二进制流作为POST内容,设置的Content-Type为image/jpeg,这种方式PHP不会将其解析到$_FILES中,所以服务器端看不到数据。
ESP32代码修正方案
要让ESP32发送符合multipart/form-data格式的请求,需手动构造包含"image"字段的请求体、boundary分隔符及文件内容。修改后的代码示例:
#define _SERVER_URL "http://X.X.X.X/upload.php" // 定义唯一的boundary,用于分隔multipart内容段 #define BOUNDARY "----WebKitFormBoundary7MA4YWxkTrZu0gW" esp_http_client_config_t _server_config = { .url = _SERVER_URL, .method = HTTP_METHOD_POST, .event_handler = _server_httpEventHandler }; esp_http_client_handle_t _server_client = NULL; void server_sendJpeg(uint8_t* jpeg, uint32_t jpegSize) { // 计算multipart请求体总长度 size_t boundary_len = strlen(BOUNDARY); size_t header_len = snprintf(NULL, 0, "--%s\r\nContent-Disposition: form-data; name=\"image\"; filename=\"cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n", BOUNDARY); size_t footer_len = snprintf(NULL, 0, "\r\n--%s--\r\n", BOUNDARY); size_t total_len = header_len + jpegSize + footer_len; // 分配内存存储完整请求体 uint8_t* post_data = malloc(total_len); if (!post_data) { // 内存分配失败处理 return; } // 拼接请求体头部 size_t offset = sprintf((char*)post_data, "--%s\r\nContent-Disposition: form-data; name=\"image\"; filename=\"cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n", BOUNDARY); // 复制JPEG数据 memcpy(post_data + offset, jpeg, jpegSize); offset += jpegSize; // 拼接尾部 sprintf((char*)post_data + offset, "\r\n--%s--\r\n", BOUNDARY); _server_client = esp_http_client_init(&_server_config); esp_http_client_set_method(_server_client, HTTP_METHOD_POST); // 设置正确的Content-Type,带上boundary char content_type[100]; snprintf(content_type, sizeof(content_type), "multipart/form-data; boundary=%s", BOUNDARY); esp_http_client_set_header(_server_client, "Content-Type", content_type); // 设置请求体长度 char total_len_str[10]; snprintf(total_len_str, sizeof(total_len_str), "%u", total_len); esp_http_client_set_header(_server_client, "Content-Length", total_len_str); // 设置POST请求体 esp_http_client_set_post_field(_server_client, (const char*)post_data, total_len); esp_http_client_perform(_server_client); // 释放内存并清理客户端 free(post_data); esp_http_client_cleanup(_server_client); }
服务器端调试方法
除打印$_FILES和$_POST外,还有这些实用调试手段:
- 读取原始请求体:用
file_get_contents('php://input')获取完整POST原始数据,直接写入日志查看ESP32发送的内容格式是否正确:$raw_data = file_get_contents('php://input'); file_put_contents('raw_request.txt', $raw_data); - 打印请求头:用
getallheaders()获取所有请求头,检查Content-Type、Content-Length等参数是否符合要求:$headers = getallheaders(); file_put_contents('request_headers.txt', var_export($headers, true)); - 抓包对比:在服务器或ESP32所在网络用tcpdump/Wireshark抓包,对比Python和ESP32发送的请求差异,重点看请求头格式、请求体结构是否一致。
补充注意事项
- boundary要保证在请求体中唯一,避免和JPEG文件内容重复;
- 内存分配后务必释放,避免ESP32内存泄漏;
- 可在ESP32的
event_handler中打印请求响应码和响应内容,确认服务器返回信息,辅助排查问题。
内容的提问来源于stack exchange,提问作者Terror404
相关产品推荐
相关产品推荐

