如何用cpp-httplib高效构建静态文件服务的?

摘要:cpp-httplib 的静态文件服务依赖内置 MIME 映射,对 .md 等未识别类型会触发下载,而手动设置 Content-Type: textmarkdown 可使其在浏览器中直接显示。
1. 引言 静态文件(Static Files) 是指那些内容在服务器上预先写好、不会随请求动态改变的文件,例如: HTML 页面(如 index.html) CSS 样式表(如 style.css) JavaScript 脚本(如 app.js) 图片(如 logo.png, photo.jpg) 字体、图标、PDF、音视频等 静态文件服务(Static File Serving) 是指 HTTP 服务器能够接收客户端(如浏览器)对某个路径的请求,并自动从本地文件系统中找到对应文件,将其内容返回给客户端。例如: 用户访问 http://example.com/style.css 服务器自动去磁盘找 ./www/style.css 读取文件内容,设置正确的 Content-Type: text/css 把文件原样返回给浏览器 这个过程无需写任何业务逻辑代码,由 HTTP 服务器框架自动完成。 一个通用的 HTTP 服务器框架通常会提供静态文件服务功能,因为: 实现简单:读文件 + 设置 MIME 类型即可; 用途广泛:极大简化小型项目部署; 开发体验好:不用额外起 Nginx 就能跑完整 Web 应用; 符合 HTTP 本质:HTTP 最初就是为传输静态文档设计的(超文本传输协议)。 2. 实例 使用 cpp-httplib 发布静态文件服务非常简单: #include <httplib.h> #include <iostream> #include <string> using namespace std; int main() { httplib::Server svr; svr.set_mount_point("/public", "D:/Work/HttpServer/public"); std::cout << "Server listening on http://0.0.0.0:8080/public\n"; svr.listen("0.0.0.0", 8080); return 0; } 在 D:/Work/HttpServer/public 这个目录中笔者放置了一些文件: 那么可以在浏览器通过 URL 地址 http://127.0.0.1:8080/public/最小二乘问题详解1:线性最小二乘/meta.json 访问到具体的文件内容: 那么是不是所有的文件都支持访问并且加载显示呢?这取决于 HTTP 服务器(cpp-httplib)对文件扩展名与 MIME 类型的映射能力: 扩展名 MIME 类型 扩展名 MIME 类型 css text/css mpga audio/mpeg csv text/csv weba audio/webm txt text/plain wav audio/wave vtt text/vtt otf font/otf html, htm text/html ttf font/ttf apng image/apng woff font/woff avif image/avif woff2 font/woff2 bmp image/bmp 7z application/x-7z-compressed gif image/gif atom application/atom+xml png image/png pdf application/pdf svg image/svg+xml mjs, js text/javascript webp image/webp json application/json ico image/x-icon rss application/rss+xml tif image/tiff tar application/x-tar tiff image/tiff xhtml, xht application/xhtml+xml jpeg, jpg image/jpeg xslt application/xslt+xml mp4 video/mp4 xml application/xml mpeg video/mpeg gz application/gzip webm video/webm zip application/zip mp3 audio/mp3 wasm application/wasm 3. 扩展 set_mount_point 是 cpp-httplib 用于发布静态文件服务的接口,将设置的目录挂载到 HTTP Get 请求。如果将代码写的更加本质一点,就是读取相应的数据,将数据填充到 Get 请求返回: #include <httplib.h> #include <filesystem> // C++17 #include <fstream> #include <iostream> #include <nlohmann/json.hpp> #include <string> using namespace std; namespace fs = std::filesystem; // 辅助函数:根据文件扩展名返回 MIME 类型 std::string get_mime_type(const std::string& filename) { if (filename.ends_with(".md")) { return "text/markdown; charset=utf-8"; } else if (filename.ends_with(".json")) { return "application/json; charset=utf-8"; } else if (filename.ends_with(".txt")) { return "text/plain; charset=utf-8"; } // 默认 fallback return "application/octet-stream"; } int main() { httplib::Server svr; std::u8string docRoot = u8"D:/Work/HttpServer/public/最小二乘问题详解1:线性最小二乘"; // 路由:GET /files/<filename> svr.Get(R"(/files/([^/]+))", [&](const httplib::Request& req, httplib::Response& res) { // 提取文件名(来自正则捕获组) std::string filename = req.matches[1]; std::u8string u8Filename(filename.begin(), filename.end()); // 安全检查:防止路径穿越(如 ../../etc/passwd) if (filename.find("..") != std::string::npos || filename.empty()) { res.status = 400; res.set_content(R"({"error": "Invalid filename"})", "application/json"); return; } // 构建完整路径 fs::path filepath = fs::path(docRoot) / u8Filename; // 检查文件是否存在且是普通文件 if (!fs::exists(filepath) || !fs::is_regular_file(filepath)) { res.status = 404; res.set_content(R"({"error": "File not found"})", "application/json"); return; } // 读取文件内容 std::ifstream file(filepath, std::ios::binary); if (!file) { res.status = 500; res.set_content(R"({"error": "Failed to read file"})", "application/json"); return; } std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); // 设置 Content-Type std::string mime = get_mime_type(filename); res.set_content(content, mime); }); std::cout << "Server listening on http://0.0.0.0:8080/files/<filename>\n"; svr.listen("0.0.0.0", 8080); return 0; } 如果我们在浏览器地址访问http://127.0.0.1:8080/files/最小二乘问题详解1:线性最小二乘.md,就可以看到最小二乘问题详解1:线性最小二乘.md这个文件的内容: 这是因为这里将 .md 文件的 MIME 设置成了 text/markdown,浏览器会按照文本格式显示 .md 文件的内容。而在 set_mount_point 接口中,由于不识别 .md 格式类型,MIME 类型会回退回 application/octet-stream ,也就是二进制文件/未知类型,浏览器会强制下载。