静态文件(Static Files) 是指那些内容在服务器上预先写好、不会随请求动态改变的文件,例如:
静态文件服务(Static File Serving) 是指 HTTP 服务器能够接收客户端(如浏览器)对某个路径的请求,并自动从本地文件系统中找到对应文件,将其内容返回给客户端。例如:
这个过程无需写任何业务逻辑代码,由 HTTP 服务器框架自动完成。
一个通用的 HTTP 服务器框架通常会提供静态文件服务功能,因为:
使用 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 | 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 |
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 ,也就是二进制文件/未知类型,浏览器会强制下载。