http协议同时传输文本和数据的新理解

发布于:2025-06-13 ⋅ 阅读:(19) ⋅ 点赞:(0)

首先,承认本人对于http协议认知确实不够,从来没有仔细研究这一块。

其次,这回确实要把自己十几年的理解更新一下了,主要还是自己过去没有认真研究过http协议。

这一次是这么回事,碰到一个情况,要在一次消息中传输文本和照片,这个按说很常见,过去写网页都是表单中就直接用了,也没有碰到过自己封装协议的情况。

这回呢,因为客户端是单片机,单片机这块之前也是单独提交要么文本,要么图片,没有一次性传过多种数据。

然后呢,我让kimi给我代码,结果给我的是流指针的方式

kimi错误方案一:

// 获取底层 WiFiClient 对象
  WiFiClient *stream = http.getStreamPtr();
  if (stream) {
    // 发送文本部分
    stream->print(body);

    // 发送图片数据
    stream->write(current_fb->buf, current_fb->len);

    // 发送结束边界
    stream->print(endBoundary);

WiFiClient 流指针在esp32单片机实测,压根没法正确初始化,至于原因,就不想找了,怎么测都是不行的,初始化后都是false。既然库中有这个功能,按说是能用的,也可能是我单片机中性芯片的问题。

下来事情呢,我就想自己拼接字符串给服务器。

kimi又给我错误方案二:

// 构建请求体
  String body;
  body += "--" + boundary + "\r\n";
  body += "Content-Disposition: form-data; name=\"message\"\r\n\r\n";
  body += photo_text + "\r\n";

  body += "--" + boundary + "\r\n";
  body += "Content-Disposition: form-data; name=\"image\"; filename=\"photo.jpg\"\r\n";
  body += "Content-Type: image/jpeg\r\n\r\n";

  String endBoundary = "\r\n--" + boundary + "--\r\n";

  // 计算总请求体长度
  size_t totalLen = body.length() + current_fb->len + endBoundary.length();

  // 设置请求体长度
  http.addHeader("Content-Length", String(totalLen));

  // 构建完整的请求体
  String fullBody = body;
  fullBody += String((char*)current_fb->buf, current_fb->len); // 直接将图片的二进制数据追加到字符串中

因为  fullBody += String((char*)current_fb->buf, current_fb->len); 其实实际加入协议的是字符串化的二进制,php服务器端 $_FILES['image'];读取的是二进制,所以肯定对不上,就会报错误3.

这个事情说明,不要完全依赖于AI,他会反复给一个错误的方向,你怎么都调不通,最终没有办法,只好自己亲自下场把内容研究透彻。

经过我和kimi的唇枪舌战,kimi终于开窍了,给我了第三个方案。【这里我要骂AI了】

// 发送HTTP POST请求
  Serial.println("Sending HTTP POST request");

  HTTPClient http;
  http.begin(serverURL);

  String boundary = "----ESP32_CAMERA_BOUNDARY";
  http.addHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
  http.addHeader("User-Agent", "ESP32-Camera");
  http.setTimeout(60000);

  // 构建请求体的文本部分
  String body;
  body += "--" + boundary + "\r\n";
  body += "Content-Disposition: form-data; name=\"message\"\r\n\r\n";
  body += photo_text + "\r\n";

  body += "--" + boundary + "\r\n";
  body += "Content-Disposition: form-data; name=\"image\"; filename=\"photo.jpg\"\r\n";
  body += "Content-Type: image/jpeg\r\n\r\n";

  String endBoundary = "\r\n--" + boundary + "--\r\n";

  // 计算总请求体长度
  size_t totalLen = body.length() + current_fb->len + endBoundary.length();

  // 设置请求体长度
  http.addHeader("Content-Length", String(totalLen));

  // 构建完整的请求体
  http.addHeader("Content-Type", "multipart/form-data; boundary=" + boundary);

  // 发送请求
  http.begin(serverURL);
  int httpCode = http.sendRequest("POST", body.c_str(), body.length());

  if (httpCode == 200) {
      // 文本部分发送成功,现在发送图片数据
      http.write(current_fb->buf, current_fb->len);
      http.write(endBoundary.c_str(), endBoundary.length());
      Serial.println("Image uploaded successfully");
  } else {
      Serial.println("Failed to send text part of the request");
  }

  http.end();

  Serial.println("arduino HTTP request completed");

这是一个通过两次发送数据,达到目的的一个方案,这个我是可以接受的,毕竟在同一个函数任务中,但是这么一个方案明明很简单为啥AI给我搞了两天,我和他激辩很久,才告诉我。。。好吧,果然人还是要靠自己。

其实吧,这个事情还有很多解决方案,比如base64,这是经常用的,再比如使用ArduinoHttpClient库,只是这次就想死磕多类型数据协议封装,才搞了好几天。

=============================

说一下最终决定写本文的目的吧。之所以被AI的这几套方案套了三天了,根源在于自己对于http的理解有问题。也是因为没有完全了解它。

现在更正一下我的认知,我一直以为http就是一去一回的,就是送过去,返回结果。

因为用惯了api什么的,写惯了网页,之前都是这么用的,最惭愧的是以前给小弟也是这么讲的(此处脸部温度180度)。尽管我很早,在20年前就知道长连接这个事情,但是默认的访问我一直认知上认为是一去一回,然后通讯中断。

直到今天(再次脸红),我和AI沟通了原因,http1.0的协议确实默认不支持长连接,都是一去一回就完事了。本人最早用http差不多在2003年,而http1.1是1997年就出来了,所以最早的对浏览器的认知确实是有问题的,十几年来一直认为默认就是一去一回,而现在的协议肯定都是http1.1的,问了ai默认都是http1.1,也就是早就支持默认长连接,断开的话需要代码控制断开。

这次去底层拼接,才搞明白这个事,真的是愚钝了。(脸红+1)