| .. | ||
| HtmlToPdfService.Admin | ||
| HtmlToPdfService.Api | ||
| HtmlToPdfService.Client | ||
| HtmlToPdfService.Core | ||
| HtmlToPdfService.Infrastructure | ||
| HtmlToPdfService.Queue | ||
| HtmlToPdfService.Tests | ||
| docker-compose.dev.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| HtmlToPdfService.sln | ||
| README.md | ||
HTML to PDF Service v2.0
高性能 HTML/URL 转 PDF/图片 微服务,基于 .NET 9 和 PuppeteerSharp。
功能特性
核心功能
- ✅ HTML/URL 转 PDF
- ✅ HTML/URL 转图片(PNG/JPEG/WebP)
- ✅ 异步任务队列(Redis)
- ✅ 同步转换(MVP 兼容)
- ✅ 浏览器池管理(自愈、健康检查、内存阈值重启)
- ✅ 批量任务提交
- ✅ 任务回调(含重试、HMAC 签名)
安全特性
- ✅ API Key 认证
- ✅ 请求限流(IP/用户维度)
- ✅ SSRF 防护
- ✅ 幂等性支持(Idempotency-Key)
运维特性
- ✅ Prometheus 指标
- ✅ 健康检查(ready/live 探针)
- ✅ 管理后台 API
- ✅ Docker 部署
- ✅ 结构化日志(Serilog)
技术栈
- .NET 9
- PuppeteerSharp 20.x
- Redis(任务队列)
- Prometheus(监控)
- Docker / Docker Compose
快速开始
Docker Compose 部署
cd src
docker-compose up -d
服务启动后访问:
- API: http://localhost:5000
- Swagger: http://localhost:5000/swagger
- 健康检查: http://localhost:5000/health
本地开发
# 启动 Redis
docker run -d --name redis -p 6379:6379 redis:7-alpine
# 进入 src 目录
cd src
# 还原依赖
dotnet restore
# 运行服务
dotnet run --project HtmlToPdfService.Api
API 接口
异步任务接口(推荐)
# 提交 PDF 转换任务
curl -X POST http://localhost:5000/api/tasks/pdf \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "html", "content": "<h1>Hello</h1>" },
"callback": { "url": "https://your-callback-url.com/webhook" }
}'
# 提交图片转换任务
curl -X POST http://localhost:5000/api/tasks/image \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "url", "content": "https://www.baidu.com/" },
"options": { "format": "png", "fullPage": true }
}'
# 查询任务状态
curl http://localhost:5000/api/tasks/{taskId}
# 下载结果
curl -O http://localhost:5000/api/tasks/{taskId}/download
# 取消任务
curl -X DELETE http://localhost:5000/api/tasks/{taskId}
# 批量提交
curl -X POST http://localhost:5000/api/tasks/batch \
-H "Content-Type: application/json" \
-d '{
"tasks": [
{ "type": "pdf", "source": { "type": "html", "content": "<h1>Doc 1</h1>" } },
{ "type": "pdf", "source": { "type": "html", "content": "<h1>Doc 2</h1>" } }
]
}'
# 预检(Dry-run)
curl -X POST http://localhost:5000/api/tasks/validate \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "url", "content": "https://example.com" }
}'
同步接口(MVP 兼容)
# HTML 转 PDF(同步)
curl -X POST http://localhost:5000/api/pdf/convert/html \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Hello World</h1>"}' \
-o document.pdf
# URL 转 PDF(同步)
curl -X POST http://localhost:5000/api/pdf/convert/url \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}' \
-o document.pdf
# HTML 转图片(同步)
curl -X POST http://localhost:5000/api/image/convert/html \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Hello</h1>", "options": {"format": "png"}}' \
-o screenshot.png
系统接口
# 健康检查
curl http://localhost:5000/health
# 就绪探针
curl http://localhost:5000/health/ready
# 存活探针
curl http://localhost:5000/health/live
# Prometheus 指标
curl http://localhost:5000/metrics
管理接口
# 仪表盘数据
curl http://localhost:5000/api/admin/dashboard
# 任务列表
curl http://localhost:5000/api/admin/tasks
# 处理卡死任务
curl -X POST http://localhost:5000/api/admin/tasks/handle-stalled
# 清理过期任务
curl -X POST "http://localhost:5000/api/admin/tasks/cleanup?retentionDays=7"
# 浏览器池状态
curl http://localhost:5000/api/admin/browser-pool
# 预热浏览器池
curl -X POST http://localhost:5000/api/admin/browser-pool/warmup
配置说明
主要配置项在 appsettings.json 中:
{
"PdfService": {
"BrowserPool": {
"MaxInstances": 10,
"MinInstances": 2,
"MaxConcurrent": 5,
"MaxTasksPerBrowserInstance": 100,
"BrowserRestartMemoryMb": 600
},
"TaskQueue": {
"Type": "Redis",
"Redis": { "ConnectionString": "localhost:6379" },
"WorkerCount": 5
},
"Storage": {
"Type": "Local",
"LocalPath": "/app/files"
},
"Security": {
"RequireAuthentication": false,
"BlockPrivateNetworks": true
},
"RateLimit": {
"Enabled": true,
"RequestsPerMinutePerIp": 60
}
}
}
存储配置
支持三种存储类型:Local(本地)、OSS(阿里云)、COS(腾讯云)
本地存储(默认)
{
"Storage": {
"Type": "Local",
"LocalPath": "/app/files",
"RetentionHours": 168,
"AutoCleanup": true
}
}
阿里云 OSS 存储
{
"Storage": {
"Type": "OSS",
"SaveLocalCopy": false,
"OSS": {
"Endpoint": "oss-cn-hangzhou.aliyuncs.com",
"AccessKeyId": "your-access-key-id",
"AccessKeySecret": "your-access-key-secret",
"BucketName": "your-bucket-name",
"CdnDomain": "",
"UseHttps": true,
"PathPrefix": "htmltopdf",
"UrlExpireSeconds": 3600
}
}
}
腾讯云 COS 存储
{
"Storage": {
"Type": "COS",
"SaveLocalCopy": false,
"COS": {
"Region": "ap-guangzhou",
"SecretId": "your-secret-id",
"SecretKey": "your-secret-key",
"BucketName": "your-bucket-1250000000",
"CdnDomain": "",
"UseHttps": true,
"PathPrefix": "htmltopdf",
"UrlExpireSeconds": 3600
}
}
}
配置说明:
Type: 存储类型,可选Local、OSS、COSSaveLocalCopy: 使用云存储时是否同时保存本地副本CdnDomain: CDN 加速域名(可选)PathPrefix: 存储路径前缀UrlExpireSeconds: 签名 URL 过期时间(秒),0 表示永不过期
项目结构
src/
├── HtmlToPdfService.Api/ # Web API 层
│ ├── Controllers/ # API 控制器
│ ├── Middleware/ # 中间件
│ └── Program.cs # 入口点
├── HtmlToPdfService.Core/ # 核心业务层
│ ├── Services/ # PDF/图片转换服务
│ ├── Pool/ # 浏览器池
│ ├── Storage/ # 文件存储
│ └── Callback/ # 回调服务
├── HtmlToPdfService.Queue/ # 任务队列层
│ ├── Models/ # 任务模型
│ └── Workers/ # 后台 Worker
├── HtmlToPdfService.Infrastructure/# 基础设施层
│ ├── Authentication/ # 认证
│ ├── RateLimit/ # 限流
│ ├── Security/ # 安全
│ └── Monitoring/ # 监控
└── HtmlToPdfService.Tests/ # 测试
├── Unit/ # 单元测试
└── Integration/ # 集成测试
运行测试
cd src
dotnet test
License
MIT