You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP8266内存不足:如何分块发送大文件作为POST请求?

分块发送大文件到dpaste.com的实现方案

由于设备内存有限(仅40KB),无法一次性加载400KB文件,不能依赖HTTPClient默认的POST方法(会将整个请求体缓存到内存),需要直接通过WiFiClientSecure手动构建分块传输请求。

实现思路

  1. 跳过HTTPClient的内存缓存逻辑,直接用WiFiClientSecure建立SSL连接
  2. 发送包含Transfer-Encoding: chunked的请求头,告知服务器采用分块传输
  3. 分块读取文件内容,每次读取大小控制在设备内存余量内(比如32KB)
  4. 按照HTTP分块编码格式逐个发送数据块,最后发送结束标记
  5. 适配application/x-www-form-urlencoded格式,对表单内容做URL编码处理

完整代码示例

#include <WiFiClientSecure.h>
#include <SPIFFS.h>

// 辅助函数:发送单个分块(遵循分块编码格式)
void send_chunk(WiFiClientSecure *client, const char* data, size_t length) {
  // 发送块长度(十六进制格式)
  client->print(String(length, HEX));
  client->print("\r\n");
  // 发送块内容
  client->print(data);
  client->print("\r\n");
}

// 辅助函数:URL编码(适配表单格式要求)
String urlEncode(const String& str) {
  String encodedString = "";
  char c;
  char code0;
  char code1;
  for (unsigned int i = 0; i < str.length(); i++) {
    c = str.charAt(i);
    if (c == ' ') {
      encodedString += '+';
    } else if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
      encodedString += c;
    } else {
      code1 = (c & 0xf) + '0';
      if ((c & 0xf) > 9) code1 = (c & 0xf) - 10 + 'A';
      c = (c >> 4) & 0xf;
      code0 = c + '0';
      if (c > 9) code0 = c - 10 + 'A';
      encodedString += '%';
      encodedString += code0;
      encodedString += code1;
    }
  }
  return encodedString;
}

// 分块发送大文件到dpaste的核心函数
void dpaste_send_large_file(WiFiClientSecure *client, String title, const char* filePath) {
  // 建立与dpaste服务器的SSL连接
  if (!client->connect("dpaste.com", 443)) {
    Serial.println("[ERROR] 连接dpaste服务器失败");
    return;
  }

  // 发送HTTP请求头
  client->print("POST /api/ HTTP/1.1\r\n");
  client->print("Host: dpaste.com\r\n");
  client->print("Authorization: TOKEN\r\n"); // 替换为你的实际授权令牌
  client->print("Content-Type: application/x-www-form-urlencoded\r\n");
  client->print("Transfer-Encoding: chunked\r\n");
  client->print("\r\n"); // 头信息结束标记

  // 打开要发送的文件(示例用SPIFFS,若用SD卡可替换为SD库)
  File file = SPIFFS.open(filePath, "r");
  if (!file) {
    Serial.println("[ERROR] 无法打开目标文件");
    client->stop();
    return;
  }

  // 发送表单前缀:content=
  String formPrefix = "content=";
  send_chunk(client, formPrefix.c_str(), formPrefix.length());

  // 分块读取并发送文件内容
  const size_t chunkSize = 32768; // 32KB,小于设备40KB内存预留余量
  char buffer[chunkSize];
  size_t bytesRead;

  while ((bytesRead = file.readBytes(buffer, chunkSize)) > 0) {
    // 对当前文件块进行URL编码(若文件无特殊字符可跳过此步节省资源)
    String encodedChunk = urlEncode(String(buffer, bytesRead));
    send_chunk(client, encodedChunk.c_str(), encodedChunk.length());
  }

  file.close();

  // 发送表单后缀:&title=XXX&expiry_days=30
  String formSuffix = "&title=" + urlEncode(title) + "&expiry_days=30";
  send_chunk(client, formSuffix.c_str(), formSuffix.length());

  // 发送分块结束标记
  client->print("0\r\n\r\n");

  // 读取并打印服务器响应
  Serial.println("[DEBUG] 服务器响应:");
  while (client->connected() || client->available()) {
    if (client->available()) {
      String line = client->readStringUntil('\n');
      Serial.println(line);
    }
  }

  // 关闭连接
  client->stop();
}

// 在setup中初始化SPIFFS(示例)
void setup() {
  Serial.begin(115200);
  // 初始化SPIFFS(若首次使用需格式化)
  if (!SPIFFS.begin(true)) {
    Serial.println("[ERROR] SPIFFS初始化失败");
    return;
  }
  // 此处添加WiFi连接代码...
}

关键注意事项

  • 内存控制:chunkSize设置为32KB,确保单块数据占用内存不超过设备的40KB限制,避免内存溢出
  • URL编码:如果文件内容包含&、=、%等特殊字符,必须编码,否则会破坏表单结构;纯文本无特殊字符可跳过编码
  • 令牌替换:将代码中的TOKEN替换为你的dpaste授权令牌
  • 文件系统:示例使用SPIFFS存储文件,若使用SD卡,需替换为SD库的文件操作逻辑
  • 分块格式:严格遵循HTTP分块编码规则,每个块的长度必须是十六进制,末尾必须发送0\r\n\r\n表示传输结束

内容的提问来源于stack exchange,提问作者Valentino Zaffrani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 06:48:26