静态文件服务
简介
HttpServer 内置静态文件服务功能,当配置 WebRoot 后,会自动处理静态资源请求,无需编写控制器。
版本变更说明
自 vbman 1.0.0.419 起,WebRoot 从 Start() 方法中独立为链式函数。旧版写法 Server.Start 8080, "C:\WebRoot" 不再支持,请改为 Server.WebRoot("C:\WebRoot").Start 8080。
快速配置
vb
Private Sub Form_Load()
Set Server = New cHttpServer
' 配置静态文件根目录
Server.WebRoot("C:\WebRoot").Start 8080
Debug.Print "静态文件服务: http://localhost:8080/"
End Sub目录结构示例
C:\WebRoot\
├── index.html # 首页
├── favicon.ico # 网站图标
├── css\
│ ├── style.css
│ └── theme.css
├── js\
│ ├── app.js
│ └── utils.js
├── images\
│ ├── logo.png
│ └── banner.jpg
└── upload\
└── avatar.png请求映射
| 请求 URL | 映射到文件 |
|---|---|
/ | C:\WebRoot\index.html |
/css/style.css | C:\WebRoot\css\style.css |
/js/app.js | C:\WebRoot\js\app.js |
/images/logo.png | C:\WebRoot\images\logo.png |
支持的 MIME 类型
vb
' 系统自动识别以下文件类型的 Content-Type
' 文本类型
text/html -> .html, .htm
text/css -> .css
text/javascript -> .js
text/plain -> .txt
' 图片类型
image/png -> .png
image/jpeg -> .jpg, .jpeg
gif/image -> .gif
image/svg+xml -> .svg
image/x-icon -> .ico
' 应用类型
application/json -> .json
application/xml -> .xml
application/pdf -> .pdf
' 字体类型
font/woff2 -> .woff2
font/woff -> .woff默认文档机制
当请求路径对应一个目录时(如访问根路径 /),HttpServer 会自动查找默认文档,按优先级逐一尝试:
| 优先级 | 默认文档 |
|---|---|
| 1 | index.html |
| 2 | index.htm |
| 3 | default.html |
| 4 | default.htm |
处理流程
请求 "/" 或 "/subdir/"
│
▼
检查 WebRoot + 路径 是否为目录
│
├─> 是目录 → 遍历默认文档列表
│ ├─> index.html 存在? → 返回文件 ✅
│ ├─> index.htm 存在? → 返回文件 ✅
│ ├─> default.html 存在? → 返回文件 ✅
│ ├─> default.htm 存在? → 返回文件 ✅
│ └─> 都不存在 → 返回 403 "Directory listing not allowed" 🔒
│
└─> 不是目录 → 返回 404添加自定义默认文档
vb
Private Sub Form_Load()
Set Server = New cHttpServer
' 添加自定义默认文档(如 home.html)
Server.AddDefaultDocument "home.html"
' 重复添加会自动忽略,不会重复入列
Server.WebRoot("C:\WebRoot").Start 8080
End Sub添加后,默认文档查找顺序变为:index.html → index.htm → default.html → default.htm → home.html
目录重定向
当请求路径对应物理目录但不以 / 结尾时,HttpServer 会自动 302 重定向到带 / 的路径:
请求 /subdir (对应物理目录)
→ 302 重定向到 /subdir/
→ 再按默认文档机制查找 /subdir/index.html 等注意:根路径
/不需要重定向,直接进入默认文档查找。
与路由的优先级关系
如果为根路径 / 注册了手动路由,路由优先执行,不会进入默认文档查找:
vb
' 路由优先:根路径由控制器处理
Call Server.Router.Add("/", "Home@Index")
' 此时访问 "/" → 调用 Home.Index,不查找 index.html优先级说明
静态文件优先于路由匹配:
请求 /index.html
│
├──> 检查 C:\WebRoot\index.html 是否存在
│ ├─> 存在 -> 返回静态文件
│ └─> 不存在 -> 进入路由匹配
│
└──> 匹配路由 /index.html混合模式(静态文件 + API)
vb
Private Sub Form_Load()
Set Server = New cHttpServer
' ========== 配置路由 ==========
' API 控制器
Call Server.Router.Reg("Api", New cApiController)
Call Server.Router.Add("/api/users", "Api@Users", OnlyGet)
Call Server.Router.Add("/api/data", "Api@Data", OnlyGet)
' 业务控制器
Call Server.Router.Reg("User", New cUserController)
Call Server.Router.Add("/user/login", "User@Login", OnlyPost)
' ========== 启动服务 ==========
' WebRoot 用于静态文件,API 请求会进入路由
Server.WebRoot("C:\WebRoot").Start 8080
Debug.Print "服务启动成功"
Debug.Print " 前端: http://localhost:8080/"
Debug.Print " API: http://localhost:8080/api/users"
End Sub单页应用 (SPA) 支持
对于 React/Vue/Angular 等 SPA,需要配置所有路由都返回 index.html:
vb
' cSpaController.cls
Public Sub Index(ctx As cHttpServerContext)
' 返回 index.html 让前端路由处理
ctx.Response.File "/index.html"
End Sub
' 注册路由
Private Sub Form_Load()
Set Server = New cHttpServer
' API 路由
Call Server.Router.Reg("Api", New cApiController)
Call Server.Router.Add("/api/*", "Api@Handle")
' SPA 路由:所有非 API 请求返回 index.html
Call Server.Router.Reg("Spa", New cSpaController)
Call Server.Router.Add("/*", "Spa@Index")
Server.WebRoot("C:\WebRoot").Start 8080
End Sub文件上传目录
vb
' 上传文件保存到静态目录
Public Sub Upload(ctx As cHttpServerContext)
' 保存上传的文件
Dim savePath As String
savePath = ctx.Server.WebRoot & "\upload\" & filename
Call SaveUploadFile(ctx.Request.RawBodyBin, savePath)
' 返回可访问的 URL
Dim result As New Dictionary
result("url") = "/upload/" & filename
ctx.Response.Json result
End Sub缓存控制
内置懒缓存 + ETag + 304
HttpServer 内置了三层缓存机制,自动感知文件更新,无需手动干预:
请求 /css/style.css
│
▼
1. 计算 ETag(文件修改时间 + 大小,仅FSO元数据,不读内容)
│
├─> 浏览器发送 If-None-Match 且匹配 → 304 Not Modified ✅
│ (零传输,浏览器使用本地缓存)
│
├─> 缓存命中且 ETag 未变 → 内存输出(跳过磁盘 I/O) ✅
│
├─> 缓存命中但 ETag 变化 → 重新读取 + 更新缓存 🔄
│
└─> 无缓存 → 磁盘读取 + 懒缓存(大小≤上限才加入) 📥懒缓存规则:
- 默认不缓存任何文件
- 首次访问时,如果文件大小 ≤
MaxCacheFileSize,自动加入内存缓存 - 超出大小上限的文件(如视频、大图片)始终从磁盘读取,但仍享受 ETag/304 协商缓存
- 文件在磁盘上更新后,ETag 自动变化,旧缓存自动失效并重新读取
缓存配置
vb
Private Sub Form_Load()
Set Server = New cHttpServer
' 缓存配置(可在 Start 前或运行中随时修改)
Server.MaxCacheFileSize = 1048576 ' 单文件缓存上限:1MB(默认)
Server.CacheTTLSeconds = 300 ' 缓存TTL:5分钟(默认)
' 设置为0可禁用内容缓存(仅保留ETag/304协商缓存)
Server.MaxCacheFileSize = 0
Server.WebRoot("C:\WebRoot").Start 8080
End Sub| 配置项 | 默认值 | 说明 |
|---|---|---|
MaxCacheFileSize | 1048576 (1MB) | 单文件缓存上限字节,0=禁用内容缓存 |
CacheTTLSeconds | 300 (5分钟) | 缓存TTL秒数,过期后检查ETag再决定是否刷新,0=永不过期 |
手动刷新缓存
部署新文件后,可手动刷新目录结构缓存和内容缓存:
vb
' 刷新目录结构缓存(新增/删除文件后调用)
' 不影响内容缓存,内容缓存通过ETag自动感知文件更新
Server.RefreshCache
' 清空所有文件内容缓存(强制重新从磁盘读取)
Server.ClearFileCache最佳实践:大多数场景只需
RefreshCache,内容缓存会通过 ETag 自动感知文件更新。只有当需要强制释放内存时才用ClearFileCache。
自定义中间件缓存头
vb
' cCacheMiddleware.cls
Public Sub Entry(ctx As cHttpServerContext)
' 静态文件添加浏览器缓存头
If IsStaticFile(ctx.Request.PathInfo) Then
' 缓存 1 小时
ctx.Response.Header("Cache-Control") = "public, max-age=3600"
End If
End Sub
Private Function IsStaticFile(path As String) As Boolean
Dim ext As String
ext = LCase(Mid(path, InStrRev(path, ".")))
IsStaticFile = (ext = ".css" Or ext = ".js" Or ext = ".png" Or _
ext = ".jpg" Or ext = ".gif" Or ext = ".ico")
End Function最后更新: 2026-06-22