# netty-proxy-spring-boot
**Repository Path**: a-crud-boy/netty-proxy-spring-boot
## Basic Information
- **Project Name**: netty-proxy-spring-boot
- **Description**: 类似nginx的springboot starter 可以代理静态页面
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-02-05
- **Last Updated**: 2026-06-14
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Netty 仿Nginx 静态文件服务器 Spring Boot Starter
基于 Netty 的高性能静态文件服务器 Spring Boot Starter,提供独立的静态资源服务能力。
## 特性
- 🚀 **高性能**:基于 Netty 异步 I/O,零拷贝文件传输
- 📦 **开箱即用**:零配置即可使用,支持 Spring Boot 自动配置
- 🔧 **灵活配置**:支持多路径映射、缓存控制、Gzip 压缩等
- 🎨 **美观界面**:内置美观的目录浏览页面
- 🌐 **SPA 支持**:完美支持单页应用(Vue、React 等)
- 🔒 **安全防护**:路径遍历防护、文件名过滤等安全机制
- 📋 **MIME 类型**:内置 40+ 种文件类型的 MIME 类型映射
## 快速开始
### 1. 添加依赖
在 `pom.xml` 中添加依赖:
```xml
com.spring.boot
netty-proxy-spring-boot-starter-2x
1.0.0
```
```xml
com.spring.boot
netty-proxy-spring-boot-starter-3x
1.0.0
```
### 2. 基础配置
在 `application.yml` 中添加配置:
```yaml
static-server:
enabled: true
port: 18080
host: 0.0.0.0
mappings:
- path: "/"
location: "D:/dist"
```
### 3. 启动应用
启动 Spring Boot 应用,访问 `http://localhost:18080` 即可查看静态文件。
## 配置说明
### 服务器配置
#### 单服务器模式
当未配置 `servers` 列表时,使用顶层配置作为单服务器配置:
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `enabled` | boolean | true | 是否启用静态服务器 |
| `port` | int | 18080 | 监听端口 |
| `host` | String | 0.0.0.0 | 绑定地址 |
| `server-name` | String | localhost | 服务器名称 |
| `keepalive-timeout` | int | 70 | HTTP Keep-Alive 超时时间(秒) |
| `worker-threads` | int | 0 | 工作线程数(0=CPU核心数) |
| `gzip` | boolean | true | 是否开启 Gzip 压缩 |
| `gzip-min-length` | int | 1024 | Gzip 压缩最小文件大小(字节) |
#### 多服务器模式
当配置了 `servers` 列表时,每个服务器可以独立配置,配置项与单服务器模式相同,但需要额外配置:
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `name` | String | - | 服务器名称(用于日志标识) |
| `port` | int | 18080 | 监听端口 |
| `host` | String | 0.0.0.0 | 绑定地址 |
| `server-name` | String | localhost | 服务器名称 |
| `keepalive-timeout` | int | 70 | HTTP Keep-Alive 超时时间(秒) |
| `worker-threads` | int | 0 | 工作线程数(0=CPU核心数) |
| `gzip` | boolean | true | 是否开启 Gzip 压缩 |
| `gzip-min-length` | int | 1024 | Gzip 压缩最小文件大小(字节) |
### 映射配置(通用)
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `path` | String | "/" | URL 路径前缀 |
| `location` | String | - | 本地文件系统路径(静态文件服务时必填) |
| `proxy-pass` | String | - | 反向代理目标地址(配置后走代理逻辑) |
| `index` | List | [index.html, index.htm] | 默认首页文件列表 |
| `cache-seconds` | int | 0 | 缓存时间(秒),0 表示不缓存 |
| `spa` | boolean | false | SPA 模式,找不到文件时返回 index.html |
| `auto-index` | boolean | false | 是否开启目录浏览 |
**注意**:`location` 和 `proxy-pass` 二选一,配置了 `proxy-pass` 时会走反向代理逻辑。
## 使用示例
### 示例 1:托管前端应用
```yaml
static-server:
port: 18080
gzip: true
mappings:
- path: "/"
location: "/var/www/frontend/dist"
index: ["index.html"]
spa: true
cache-seconds: 3600
```
### 示例 2:多路径映射(单服务器模式)
```yaml
static-server:
port: 18080
mappings:
- path: "/"
location: "./public"
auto-index: true
- path: "/static"
location: "./assets/static"
cache-seconds: 86400
- path: "/admin"
location: "./admin/dist"
spa: true
```
### 示例 3:图片服务
```yaml
static-server:
port: 18080
gzip: false
mappings:
- path: "/images"
location: "/data/images"
cache-seconds: 31536000 # 缓存一年
```
### 示例 4:开发环境配置
```yaml
static-server:
port: 3000
worker-threads: 4
gzip: true
mappings:
- path: "/"
location: "./src/main/resources/static"
auto-index: true
cache-seconds: 0 # 不缓存
```
## 高级特性
### SPA 模式
SPA 模式适用于 Vue、React 等单页应用。当请求的文件不存在时,服务器会自动返回 `index.html`。
```yaml
static-server:
mappings:
- path: "/"
location: "./dist"
spa: true
index: ["index.html"]
```
### 目录浏览
开启目录浏览后,访问目录时会显示文件列表:
```yaml
static-server:
mappings:
- path: "/"
location: "./files"
auto-index: true
```
### 缓存控制
设置缓存时间可以减少服务器负载:
```yaml
static-server:
mappings:
- path: "/static"
location: "./static"
cache-seconds: 3600 # 缓存 1 小时
```
### Gzip 压缩
自动压缩文本类文件(HTML、CSS、JS、JSON 等):
```yaml
static-server:
gzip: true
gzip-min-length: 1024 # 大于 1KB 的文件才压缩
```
## 支持的文件类型
### 文档
- HTML: `.html`, `.htm`
- CSS: `.css`
- JavaScript: `.js`, `.mjs`
- JSON: `.json`
- XML: `.xml`
- Markdown: `.md`
- PDF: `.pdf`
- Text: `.txt`
### 图片
- PNG: `.png`
- JPEG: `.jpg`, `.jpeg`
- GIF: `.gif`
- SVG: `.svg`
- ICO: `.ico`
- WebP: `.webp`
- AVIF: `.avif`
### 字体
- WOFF: `.woff`
- WOFF2: `.woff2`
- TTF: `.ttf`
- OTF: `.otf`
- EOT: `.eot`
### 媒体
- Audio: `.mp3`, `.wav`, `.ogg`
- Video: `.mp4`, `.webm`
### 压缩包
- ZIP: `.zip`
- GZIP: `.gz`
- TAR: `.tar`
### 其他
- WASM: `.wasm`
- Source Map: `.map`
## 安全特性
- **路径遍历防护**:防止 `../` 等路径遍历攻击
- **文件名过滤**:防止恶意文件名
- **隐藏文件保护**:不显示隐藏文件
- **URI 清理**:防止 URL 编码攻击
- **请求方法限制**:仅支持 GET 和 HEAD 请求
## 性能优化
- **零拷贝文件传输**:使用 `DefaultFileRegion` 实现高效文件传输
- **Gzip 压缩**:减少传输体积
- **HTTP Keep-Alive**:支持连接复用
- **可配置工作线程数**:根据服务器性能调整
## 扩展与插件机制
### HttpHandler 扩展点(核心处理能力)
从 `1.0.0` 版本开始,核心路由基于 `HttpHandler` 抽象进行处理分发,内置了:
- 静态文件处理:`StaticHttpHandler`(基于 `StaticFileHandler`)
- 反向代理处理:`ProxyHttpHandler`(基于 `ProxyHandler`)
- 访问日志:`AccessLogHttpHandler`(包装所有 Handler,记录访问日志)
你可以实现自己的 `HttpHandler` 来扩展功能,例如鉴权、限流、自定义业务逻辑、WebSocket 升级等:
```java
public class MyCustomHttpHandler implements HttpHandler {
@Override
public String name() {
return "my-custom";
}
@Override
public boolean supports(FullHttpRequest request, StaticMapping mapping) {
// 根据路径 / 方法 / 映射等判断是否由当前 Handler 处理
return request.uri().startsWith("/api/custom");
}
@Override
public boolean handle(ChannelHandlerContext ctx,
FullHttpRequest request,
StaticMapping mapping) throws Exception {
// 在这里完成自定义处理逻辑
// 返回 true 表示已经完全处理,后续 Handler 不再处理
// 返回 false 表示可以交给后续 Handler 继续处理
return true;
}
}
```
> 说明:当前版本的 `HttpHandler` 接口为 `supports(..) + handle(..)`,未来如果扩展为返回 `boolean` 以支持“链式过滤”,接口会保持兼容演进。
### StaticServerModule SPI 插件机制
为了方便第三方在 **不修改源码** 的情况下扩展能力,核心模块提供了基于 JDK `ServiceLoader` 的 SPI 接口:
- 接口:`com.spring.boot.staticserver.StaticServerModule`
- 上下文:`com.spring.boot.staticserver.StaticServerModuleContext`
实现步骤:
1. 在你的扩展 jar 中实现 `StaticServerModule`:
```java
public class MyStaticServerModule implements StaticServerModule {
@Override
public void initialize(StaticServerModuleContext context) {
ServerConfig serverConfig = context.getServerConfig();
// 可根据不同 serverConfig(端口 / serverName 等)决定是否注册
context.registerHttpHandler(new MyCustomHttpHandler());
}
}
```
2. 在你的 jar 中添加 SPI 声明文件:
`META-INF/services/com.spring.boot.staticserver.StaticServerModule`:
```text
com.example.MyStaticServerModule
```
3. 将你的扩展 jar 放到应用的依赖中(例如在 Spring Boot 应用的 `pom.xml` 中添加依赖)。
> 当 `NettyStaticServer` 启动时,会自动通过 `ServiceLoader` 发现并初始化所有 `StaticServerModule`,并把它们注册的 `HttpHandler` 插入到内置处理链之前。
### 典型扩展场景
- 自定义鉴权:在模块中注册一个 `HttpHandler`,在 `supports` 中匹配需要鉴权的路径,在 `handle` 中做 Token 校验,不通过则直接返回 401。
- 限流 / 熔断:实现 `HttpHandler` 或后续的 `HttpFilter`,根据 QPS、IP 等维度限制请求。
- WebSocket 支持:实现一个 `HttpHandler`,在 `supports` 中判断 `Upgrade: websocket` 请求,在 `handle` 中完成协议升级并切换到 WebSocket pipeline。
以上扩展能力对使用者是透明的:**只要引入你的扩展 jar,重新启动应用即可生效**。
## 监控和日志
服务器启动时会输出映射配置信息:
```
┌────────────────────────────────────────────────────────────┐
│ Static Server Mappings │
├────────────────────────────────────────────────────────────┤
│ ✓ / -> ./public [AutoIndex] │
│ ✓ /static -> ./assets/static │
│ ✓ /admin -> ./admin/dist [SPA] │
└────────────────────────────────────────────────────────────┘
```
## 常见问题
### Q: 如何修改默认端口?
A: 在配置文件中设置 `static-server.port`:
```yaml
static-server:
port: 18080
```
### Q: 如何禁用静态服务器?
A: 设置 `static-server.enabled` 为 `false`:
```yaml
static-server:
enabled: false
```
### Q: 如何支持 HTTPS?
A: 从当前版本开始,内置支持基于证书的 HTTPS,你可以为单个或多个 server 分别开启 SSL:
单服务器模式示例(PKCS12 证书):
```yaml
static-server:
enabled: true
port: 8443
ssl-enabled: true
ssl-key-store: classpath:certs/server.p12
ssl-key-store-password: changeit
ssl-key-store-type: PKCS12 # 可选,默认 PKCS12
mappings:
- path: "/"
location: "/var/www/https-site"
```
多服务器模式示例(为其中一个 server 开启 HTTPS):
```yaml
static-server:
enabled: true
servers:
- name: http-site
host: 0.0.0.0
port: 18080
mappings:
- path: /
location: /data/www/site-http
- name: https-site
host: 0.0.0.0
port: 18443
ssl-enabled: true
ssl-key-store: classpath:certs/server.p12
ssl-key-store-password: changeit
ssl-key-store-type: PKCS12
mappings:
- path: /
location: /data/www/site-https
```
> 证书可以使用 JKS 或 PKCS12,`ssl-key-store` 支持文件路径或 `classpath:` 前缀。启用 SSL 后,直接访问 `https://host:port` 即可。
### Q: 如何限制访问?
A: 建议使用防火墙或反向代理进行访问控制。
### Q: 如何像 Nginx 一样配置多个站点?
A: 使用 `static-server.servers`(多服务器模式)。当配置了 `servers` 列表时,会启用多服务器模式,每个服务器可以独立配置端口和映射;如果未配置 `servers` 或列表为空,则使用单服务器模式(使用顶层配置):
```yaml
static-server:
enabled: true
servers:
- name: site-a
host: 0.0.0.0
port: 18080
server-name: site-a.local
keepalive-timeout: 70
mappings:
- path: /
location: /data/www/site-a
spa: true
cache-seconds: 3600
- name: site-b
host: 0.0.0.0
port: 18081
server-name: site-b.local
mappings:
- path: /
location: /data/www/site-b
```
### Q: 如何做简单的反向代理?
A: 在 `mappings` 中使用 `proxy-pass`,当配置了 `proxy-pass` 时会走代理逻辑:
```yaml
static-server:
enabled: true
servers:
- name: api-proxy
host: 0.0.0.0
port: 19000
mappings:
- path: /
proxy-pass: http://localhost:8080
```
### Q: 文件上传后立即生效吗?
A: 是的,服务器会实时读取文件系统,无需重启。
## 技术栈
- **Netty**: netty-all(版本由 Spring Boot 依赖管理)
- **Spring Boot**: 2.5.15+ / 3.2.0+
- **Java**: 8+(Boot 2.x starter)/ 17+(Boot 3.x starter)
## 许可证
本项目采用 MIT 许可证。
## 贡献
欢迎提交 Issue 和 Pull Request!