如何用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); });
关键细节说明
- 动态路由匹配:
/LOG*会匹配所有以/LOG开头的URL,不管你的LOG文件是/LOG001.txt还是/LOG_202405.csv,都能被正确处理。 - 文件存在性检查:必须先判断SPIFFS中是否存在目标文件,避免返回404错误。
- 正确的响应生成:
request->beginResponse(SPIFFS, filePath, "application/octet-stream")会直接从SPIFFS读取文件内容,而不是返回固定字符串。application/octet-stream是通用的二进制文件MIME类型,能兼容各种格式的LOG文件。 - 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
相关产品推荐
相关产品推荐

