使用 cpp-httplib 发布静态文件服务

1. 引言

静态文件(Static Files) 是指那些内容在服务器上预先写好、不会随请求动态改变的文件,例如:

  • HTML 页面(如 index.html)

  • CSS 样式表(如 style.css)

  • JavaScript 脚本(如 app.js)

  • 图片(如 logo.png, photo.jpg)

  • 字体、图标、PDF、音视频等

静态文件服务(Static File Serving) 是指 HTTP 服务器能够接收客户端(如浏览器)对某个路径的请求,并自动从本地文件系统中找到对应文件,将其内容返回给客户端。例如:

  • 用户访问 https://uniomo.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 这个目录中笔者放置了一些文件:

1000410-20251228132035347-428601642.png

那么可以在浏览器通过 URL 地址 http://127.0.0.1:8080/public/最小二乘问题详解1:线性最小二乘/meta.json 访问到具体的文件内容:

1000410-20251228132055582-1832468144.png

那么是不是所有的文件都支持访问并且加载显示呢?这取决于 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这个文件的内容:

1000410-20251228132115989-1595464698.png

这是因为这里将 .md 文件的 MIME 设置成了 text/markdown,浏览器会按照文本格式显示 .md 文件的内容。而在 set_mount_point 接口中,由于不识别 .md 格式类型,MIME 类型会回退回 application/octet-stream ,也就是二进制文件 / 未知类型,浏览器会强制下载。


使用 cpp-httplib 发布静态文件服务
https://uniomo.com/archives/shi-yong-cpp-httplib-fa-bu-jing-tai-wen-jian-fu-wu
作者
雨落秋垣
发布于
2025年12月26日
许可协议