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

如何用AsyncWebServer实现SPIFFS文件下载并提取URL文件名?

解决ESP32 AsyncWebServer SPIFFS文件下载问题

我明白你现在的困扰:原来的代码能生成SPIFFS里LOG开头文件的下载链接,但迁移到AsyncWebServer后,虽然下载窗口能正常弹出,但内容只是固定的"Ok",完全没有实际的文件数据。这是因为你当前的响应只是返回了硬编码字符串,没有从SPIFFS读取并发送真实的文件内容。下面是完整的解决方案:

第一步:配置动态路由与文件读取逻辑

首先,你需要用动态路由来匹配所有LOG开头的文件请求,这样不管你的LOG文件叫什么名字,都能被正确捕获并处理。这里直接用通配符路由匹配/LOG*即可:

// 先确保SPIFFS正常初始化(建议放在setup()最开头)
if (!SPIFFS.begin(true)) {
  Serial.println("Failed to mount SPIFFS");
  return;
}

// 处理所有LOG开头的文件下载请求
serverAsync.on("/LOG*", HTTP_GET, [](AsyncWebServerRequest *request) {
  // 从请求URL中提取完整的文件路径(比如请求是/LOG001.txt,这里得到的就是"/LOG001.txt")
  String filePath = request->url();
  
  // 先验证文件是否存在于SPIFFS中,避免返回错误
  if (!SPIFFS.exists(filePath)) {
    request->send(404, "text/plain", "File not found");
    return;
  }

  // 提取纯文件名(用于下载时浏览器显示的文件名)
  String fileName = filePath.substring(filePath.lastIndexOf('/') + 1);

  // 生成真实的文件响应:直接从SPIFFS读取文件内容并发送
  AsyncWebServerResponse *response = request->beginResponse(SPIFFS, filePath, "application/octet-stream");
  // 设置下载头,让浏览器识别为下载操作,并指定显示的文件名
  response->addHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
  request->send(response);
});

第二步:更新下载链接生成代码

原来的链接生成逻辑基本可以保留,因为我们的路由直接匹配/LOG开头的路径,所以链接地址不需要修改:

String fileListStr;
File root = SPIFFS.open("/");
File file = root.openNextFile();
while (file) {
  if(strncmp(file.name(), "/LOG", 4) == 0) {
    fileListStr += "<a href=\"";
    fileListStr += file.name(); // 直接指向文件路径,会被上面的路由捕获
    fileListStr += "\">";
    fileListStr += file.name();
    fileListStr += "</a>";
    fileListStr += " ";
    fileListStr += file.size();
    fileListStr += "<br>\r\n";
  }
  file = root.openNextFile();
}

// 新增一个路由返回文件列表页面
serverAsync.on("/", HTTP_GET, [fileListStr](AsyncWebServerRequest *request) mutable {
  request->send(200, "text/html", fileListStr);
});

关键细节说明

  1. 动态路由匹配:/LOG*会匹配所有以/LOG开头的URL,不管你的LOG文件是/LOG001.txt还是/LOG_202405.csv,都能被正确处理。
  2. 文件存在性检查:必须先判断SPIFFS中是否存在目标文件,避免返回404错误。
  3. 正确的响应生成:request->beginResponse(SPIFFS, filePath, "application/octet-stream")会直接从SPIFFS读取文件内容,而不是返回固定字符串。application/octet-stream是通用的二进制文件MIME类型,能兼容各种格式的LOG文件。
  4. Content-Disposition头优化:除了attachment标记为下载,加上filename参数能让浏览器显示正确的文件名,而不是用URL路径作为默认文件名。

可选方案:专用下载路由

如果你想把下载请求统一放到/download路径下(比如/download/LOG001.txt),可以这样调整:

// 专用下载路由
serverAsync.on("/download/*", HTTP_GET, [](AsyncWebServerRequest *request) {
  // 提取通配符*对应的部分,拼接成完整文件路径
  String filePath = "/" + request->pathArg(0);
  
  if (!SPIFFS.exists(filePath)) {
    request->send(404, "text/plain", "File not found");
    return;
  }

  String fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
  AsyncWebServerResponse *response = request->beginResponse(SPIFFS, filePath, "application/octet-stream");
  response->addHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
  request->send(response);
});

// 对应的链接生成代码修改为:
fileListStr += "<a href=\"/download";
fileListStr += file.name(); // 最终链接为"/download/LOG001.txt"
fileListStr += "\">";

这样做的好处是把下载逻辑和普通请求分开,代码结构更清晰。

内容的提问来源于stack exchange,提问作者William Lucid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:20:43