This commit is contained in:
zpc 2025-12-11 23:52:15 +08:00
parent 09f29f01ed
commit d88f33e0a5
8 changed files with 839 additions and 5 deletions

78
.gitignore vendored
View File

@ -1,14 +1,17 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# =========================
# User-specific files
# =========================
*.suo
*.user
*.userosscache
*.sln.docstates
node_modules/*
src/HtmlToPdfService.Admin/node_modules/*
# =========================
# Build results
# =========================
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
@ -21,7 +24,11 @@ bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# =========================
# IDE and Editor files
# =========================
# Visual Studio 2015/2017/2019/2022 cache/options directory
.vs/
@ -32,7 +39,9 @@ bld/
.idea/
*.sln.iml
# =========================
# .NET Core
# =========================
project.lock.json
project.fragment.lock.json
artifacts/
@ -40,19 +49,60 @@ artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# =========================
# Node.js / Frontend
# =========================
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
dist/
.nuxt/
.output/
# =========================
# Generated files
# =========================
# PDF files (generated)
pdfs/
*.pdf
# Image files (generated output)
/src/HtmlToPdfService.Api/files/
/mvp/HtmlToPdfService.Api/pdfs/
# =========================
# NuGet Packages
# =========================
*.nupkg
*.snupkg
**/packages/*
# Local Chromium
# =========================
# Chromium / Puppeteer
# =========================
.local-chromium/
**/.local-chromium/
# =========================
# Logs and runtime files
# =========================
/src/HtmlToPdfService.Api/logs/
/mvp/HtmlToPdfService.Api/logs/
*.log
# =========================
# Environment files
# =========================
.env
.env.local
.env.*.local
appsettings.*.local.json
# =========================
# OS generated files
# =========================
.DS_Store
.DS_Store?
._*
@ -60,7 +110,25 @@ pdfs/
.Trashes
ehthumbs.db
Thumbs.db
desktop.ini
# =========================
# Test results
# =========================
TestResults/
*.trx
coverage/
*.coveragexml
/src/HtmlToPdfService.Api/files/*
/src/HtmlToPdfService.Api/logs/*
# =========================
# Docker
# =========================
docker-compose.override.yml
# =========================
# Temporary files
# =========================
*.tmp
*.temp
*.swp
*~

96
CHANGELOG.md Normal file
View File

@ -0,0 +1,96 @@
# 更新日志
所有重要更改都会记录在此文件中。
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)
版本管理遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
## [Unreleased]
### 计划中
- 支持 PDF 加密与密码保护
- 支持自定义页眉页脚模板
- 支持水印功能
- 多语言管理后台
---
## [2.0.0] - 2024-12-10
### 🎉 正式版发布
这是一个重大版本更新,引入了异步任务处理架构。
### 新增
- ✨ **异步任务队列** - 基于 Redis 的任务队列,支持高并发处理
- ✨ **任务管理** - 完整的任务生命周期管理(创建、查询、取消、重试)
- ✨ **批量任务** - 支持一次提交多个转换任务
- ✨ **管理后台** - Vue 3 构建的可视化管理界面
- ✨ **API Key 认证** - 支持多租户 API Key 管理
- ✨ **请求限流** - IP/用户维度的速率限制
- ✨ **SSRF 防护** - 阻止内网地址访问
- ✨ **幂等性支持** - Idempotency-Key 防止重复提交
- ✨ **Prometheus 监控** - 完整的指标采集
- ✨ **健康检查** - Ready/Live 探针,支持 K8s
- ✨ **云存储支持** - 支持阿里云 OSS 和腾讯云 COS
- ✨ **回调重试** - 带指数退避的回调重试机制
- ✨ **HMAC 签名** - 回调请求签名验证
### 优化
- ⚡ 浏览器池自愈机制(内存阈值重启、健康检查)
- ⚡ 任务状态新增 `Stalled`(卡死)状态
- ⚡ 多队列隔离fast/normal/slow
- ⚡ Dry-run 预检 API
### 技术栈升级
- 升级到 .NET 9
- 升级 PuppeteerSharp 到 20.x
### 兼容性
- 保留同步 API 接口,向后兼容 MVP 版本
---
## [1.0.0] - 2024-11-01
### 🚀 MVP 版本发布
首个公开版本,提供基础的 HTML/URL 转 PDF 功能。
### 新增
- ✨ HTML 内容转 PDF
- ✨ URL 转 PDF
- ✨ HTML/URL 转图片PNG/JPEG/WebP
- ✨ 浏览器实例池化
- ✨ 并发控制
- ✨ 本地文件存储
- ✨ 回调机制
- ✨ Docker 容器化部署
- ✨ 健康检查接口
### 技术栈
- .NET 8
- PuppeteerSharp 20.2.5
- Chromium (Headless)
---
## 版本对比
| 特性 | MVP (1.0) | 正式版 (2.0) |
|------|-----------|--------------|
| 接口模式 | 同步 | 异步 + 同步 |
| 任务管理 | ❌ | ✅ |
| 认证授权 | ❌ | ✅ |
| 请求限流 | ❌ | ✅ |
| 监控告警 | 基础 | 完整 |
| 云存储 | ❌ | ✅ |
| 管理后台 | ❌ | ✅ |
| 集群部署 | 有限 | 完整 |
---
[Unreleased]: https://github.com/your-username/html-to-pdf/compare/v2.0.0...HEAD
[2.0.0]: https://github.com/your-username/html-to-pdf/compare/v1.0.0...v2.0.0
[1.0.0]: https://github.com/your-username/html-to-pdf/releases/tag/v1.0.0

178
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,178 @@
# 贡献指南
感谢您对 HtmlToPdf Service 的关注!我们欢迎任何形式的贡献。
## 如何贡献
### 报告 Bug
如果你发现了 Bug请通过 [GitHub Issues](https://github.com/your-username/html-to-pdf/issues) 报告。创建 Issue 时请包含:
- 问题的详细描述
- 复现步骤
- 预期行为 vs 实际行为
- 运行环境(操作系统、.NET 版本、Docker 版本等)
- 相关的日志或错误信息
### 功能建议
有新功能想法?请创建一个 Feature Request Issue描述
- 功能的详细说明
- 使用场景
- 可能的实现方案(可选)
### 提交代码
1. **Fork 仓库**
2. **克隆你的 Fork**
```bash
git clone https://github.com/your-username/html-to-pdf.git
cd html-to-pdf
```
3. **创建功能分支**
```bash
git checkout -b feature/your-feature-name
```
4. **进行开发**
- 遵循现有的代码风格
- 添加必要的测试
- 确保所有测试通过
5. **提交更改**
```bash
git add .
git commit -m "feat: 添加某某功能"
```
6. **推送到你的 Fork**
```bash
git push origin feature/your-feature-name
```
7. **创建 Pull Request**
- 描述你的更改
- 关联相关的 Issue如果有
## 开发环境设置
### 前置要求
- .NET 9.0 SDK
- Docker & Docker Compose
- Node.js 18+(用于管理后台开发)
- Redis或使用 Docker
### 本地开发
```bash
# 启动 Redis
docker run -d --name redis -p 6379:6379 redis:7-alpine
# 进入源码目录
cd src
# 还原依赖
dotnet restore
# 运行测试
dotnet test
# 启动服务
dotnet run --project HtmlToPdfService.Api
```
### 管理后台开发
```bash
cd src/HtmlToPdfService.Admin
npm install
npm run dev
```
## 代码规范
### C# 代码规范
- 使用 4 空格缩进
- 类名使用 PascalCase
- 私有字段使用 `_camelCase` 前缀
- 方法名使用 PascalCase
- 异步方法以 `Async` 结尾
- 添加 XML 文档注释
### 提交信息规范
使用 [Conventional Commits](https://www.conventionalcommits.org/) 格式:
```
<type>(<scope>): <description>
[optional body]
[optional footer]
```
**Type 类型:**
- `feat`: 新功能
- `fix`: Bug 修复
- `docs`: 文档更新
- `style`: 代码格式(不影响功能)
- `refactor`: 重构
- `test`: 测试相关
- `chore`: 构建/工具相关
**示例:**
```
feat(api): 添加批量任务提交接口
fix(pool): 修复浏览器池内存泄漏问题
docs: 更新 API 文档
```
## 测试
### 运行测试
```bash
cd src
dotnet test
```
### 测试覆盖率
```bash
dotnet test --collect:"XPlat Code Coverage"
```
## Pull Request 检查清单
提交 PR 前请确保:
- [ ] 代码可以正常编译
- [ ] 所有测试通过
- [ ] 添加了新功能的测试(如适用)
- [ ] 更新了相关文档
- [ ] 提交信息符合规范
- [ ] 代码已格式化
## 行为准则
参与本项目,请遵守以下准则:
- 尊重所有贡献者
- 保持友善和专业的交流
- 接受建设性的批评
- 关注项目和社区的最佳利益
## 问题?
如有任何问题,欢迎通过以下方式联系:
- [GitHub Discussions](https://github.com/your-username/html-to-pdf/discussions)
- [GitHub Issues](https://github.com/your-username/html-to-pdf/issues)
再次感谢您的贡献!🎉

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2024-2025 HtmlToPdf Service Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

470
README.md Normal file
View File

@ -0,0 +1,470 @@
<div align="center">
# 🎨 HtmlToPdf Service
**高性能 HTML/URL 转 PDF/图片 微服务**
基于 .NET 9 + PuppeteerSharp + Chromium 构建
[![.NET](https://img.shields.io/badge/.NET-9.0-512BD4?style=flat-square&logo=dotnet)](https://dotnet.microsoft.com/)
[![PuppeteerSharp](https://img.shields.io/badge/PuppeteerSharp-20.x-00D8FF?style=flat-square)](https://www.puppeteersharp.com/)
[![Docker](https://img.shields.io/badge/Docker-Ready-2496ED?style=flat-square&logo=docker)](https://www.docker.com/)
[![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)](LICENSE)
[功能特性](#-功能特性) •
[快速开始](#-快速开始) •
[API 文档](#-api-接口) •
[配置说明](#-配置说明) •
[部署指南](#-部署指南) •
[贡献指南](#-贡献)
</div>
---
## ✨ 功能特性
### 核心转换能力
- 🔄 **HTML → PDF** — 支持复杂 HTML 内容转换
- 🌐 **URL → PDF** — 支持任意网页转换,完美渲染 SPAReact/Vue/Angular
- 🖼️ **HTML/URL → 图片** — 支持 PNG、JPEG、WebP 格式
- 📐 **自定义选项** — 纸张大小、方向、边距、页眉页脚等
### 高性能架构
- ⚡ **异步任务队列** — 基于 Redis 的任务队列,支持高并发
- 🔧 **浏览器池化** — 实例复用、自动重启、健康检查
- 📦 **批量处理** — 一次提交多个转换任务
- 💾 **结果缓存** — 相同内容避免重复转换
### 企业级安全
- 🔐 **API Key 认证** — 支持多租户 API Key 管理
- 🛡️ **请求限流** — IP/用户维度的速率限制
- 🚫 **SSRF 防护** — 阻止内网地址访问
- 🔑 **幂等性支持** — Idempotency-Key 防止重复提交
### 运维友好
- 📊 **Prometheus 监控** — 完整的指标采集
- 🏥 **健康检查** — Ready/Live 探针,支持 K8s
- 📝 **结构化日志** — Serilog 日志输出
- 🎛️ **管理后台** — Vue 3 构建的可视化管理界面
### 灵活存储
- 💿 **本地存储** — 适合单机部署
- ☁️ **阿里云 OSS** — 支持 CDN 加速
- ☁️ **腾讯云 COS** — 多云存储方案
---
## 📁 项目结构
```
html-to-pdf/
├── src/ # 正式版源代码
│ ├── HtmlToPdfService.Api/ # Web API 层
│ ├── HtmlToPdfService.Core/ # 核心业务层
│ ├── HtmlToPdfService.Queue/ # 任务队列层
│ ├── HtmlToPdfService.Infrastructure/ # 基础设施层
│ ├── HtmlToPdfService.Admin/ # 管理后台Vue 3
│ ├── HtmlToPdfService.Tests/ # 测试项目
│ ├── docker-compose.yml # Docker 编排文件
│ └── Dockerfile # Docker 构建文件
├── mvp/ # MVP 简化版(同步模式)
├── docs/ # 设计文档
└── README.md # 本文件
```
---
## 🚀 快速开始
### 方式一Docker Compose推荐
```bash
# 克隆项目
git clone https://github.com/your-username/html-to-pdf.git
cd html-to-pdf/src
# 启动服务
docker-compose up -d
# 查看日志
docker-compose logs -f htmltopdf
```
服务启动后访问:
- **API**: http://localhost:5000
- **Swagger**: http://localhost:5000/swagger
- **健康检查**: http://localhost:5000/health
- **管理后台**: http://localhost:5000/admin
### 方式二:本地开发
```bash
# 1. 启动 Redis
docker run -d --name redis -p 6379:6379 redis:7-alpine
# 2. 进入源码目录
cd src
# 3. 还原依赖
dotnet restore
# 4. 运行服务
dotnet run --project HtmlToPdfService.Api
```
### 方式三:使用 MVP 简化版
如果你只需要简单的同步转换功能,可以使用 MVP 版本:
```bash
cd mvp
docker-compose up -d
```
> **MVP vs 正式版对比:**
> - MVP同步模式无需 Redis适合低并发场景
> - 正式版:异步队列,支持高并发、任务管理、监控告警
---
## 📖 API 接口
### 异步任务接口(推荐)
#### 提交 PDF 转换任务
```bash
curl -X POST http://localhost:5000/api/tasks/pdf \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "html",
"content": "<h1>Hello World</h1><p>这是测试内容</p>"
},
"options": {
"format": "A4",
"printBackground": true
},
"callback": {
"url": "https://your-app.com/webhook"
}
}'
```
**响应:**
```json
{
"taskId": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "任务已创建,正在排队处理",
"estimatedWaitTime": 3
}
```
#### 查询任务状态
```bash
curl http://localhost:5000/api/tasks/{taskId}
```
#### 下载结果文件
```bash
curl -O http://localhost:5000/api/tasks/{taskId}/download
```
#### 提交图片转换任务
```bash
curl -X POST http://localhost:5000/api/tasks/image \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "url",
"content": "https://www.example.com"
},
"options": {
"format": "png",
"fullPage": true,
"width": 1920,
"height": 1080
}
}'
```
#### 批量提交任务
```bash
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>" } }
]
}'
```
### 同步接口MVP 兼容)
```bash
# 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
```
### 系统接口
```bash
# 健康检查
curl http://localhost:5000/health
# 就绪探针K8s
curl http://localhost:5000/health/ready
# Prometheus 指标
curl http://localhost:5000/metrics
```
---
## ⚙️ 配置说明
主要配置在 `appsettings.json` 中:
```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",
"RetentionHours": 168
},
"Security": {
"RequireAuthentication": false,
"BlockPrivateNetworks": true
},
"RateLimit": {
"Enabled": true,
"RequestsPerMinutePerIp": 60
}
}
}
```
### 存储配置
<details>
<summary><b>本地存储(默认)</b></summary>
```json
{
"Storage": {
"Type": "Local",
"LocalPath": "/app/files",
"RetentionHours": 168,
"AutoCleanup": true
}
}
```
</details>
<details>
<summary><b>阿里云 OSS</b></summary>
```json
{
"Storage": {
"Type": "OSS",
"OSS": {
"Endpoint": "oss-cn-hangzhou.aliyuncs.com",
"AccessKeyId": "your-access-key-id",
"AccessKeySecret": "your-access-key-secret",
"BucketName": "your-bucket-name",
"PathPrefix": "htmltopdf",
"UrlExpireSeconds": 3600
}
}
}
```
</details>
<details>
<summary><b>腾讯云 COS</b></summary>
```json
{
"Storage": {
"Type": "COS",
"COS": {
"Region": "ap-guangzhou",
"SecretId": "your-secret-id",
"SecretKey": "your-secret-key",
"BucketName": "your-bucket-1250000000",
"PathPrefix": "htmltopdf",
"UrlExpireSeconds": 3600
}
}
}
```
</details>
### 环境变量配置
所有配置都可以通过环境变量覆盖:
```bash
export PdfService__BrowserPool__MaxInstances=20
export PdfService__TaskQueue__Redis__ConnectionString=redis:6379
export PdfService__Security__RequireAuthentication=true
```
---
## 🐳 部署指南
### Docker 单实例部署
```bash
cd src
docker-compose up -d
```
### Kubernetes 部署
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: htmltopdf-service
spec:
replicas: 3
template:
spec:
containers:
- name: htmltopdf
image: htmltopdf-service:2.0
resources:
limits:
memory: "2Gi"
cpu: "2000m"
env:
- name: PdfService__TaskQueue__Redis__ConnectionString
valueFrom:
secretKeyRef:
name: redis-secret
key: connection-string
```
### 生产环境建议
| 资源 | 单实例 | 集群3实例 |
|------|--------|--------------|
| 内存 | 2-4 GB | 6-12 GB |
| CPU | 2-4 核心 | 6-12 核心 |
| 并发能力 | 50+ QPS | 150+ QPS |
---
## 📊 监控与告警
### Prometheus 指标
服务暴露 `/metrics` 端点,包含以下关键指标:
- `conversion_tasks_total` - 转换任务总数
- `conversion_duration_seconds` - 转换耗时分布
- `browser_pool_instances` - 浏览器实例状态
- `task_queue_length` - 队列长度
### Grafana Dashboard
推荐监控面板:
- 任务成功率
- 平均处理时间
- 队列积压情况
- 浏览器池使用率
---
## 🛠️ 技术栈
| 组件 | 技术 | 版本 |
|------|------|------|
| 运行时 | .NET | 9.0 |
| 浏览器控制 | PuppeteerSharp | 20.x |
| 渲染引擎 | Chromium | 自动下载 |
| 任务队列 | Redis | 7.0+ |
| 前端管理 | Vue 3 + Element Plus | 3.x |
| 容器化 | Docker | 20.x |
| 监控 | Prometheus | 2.40+ |
---
## 📈 性能指标
| 场景 | 吞吐量 | 响应时间 |
|------|--------|----------|
| 简单 HTML → PDF | 100+ QPS | < 3s |
| 复杂 HTML → PDF | 50+ QPS | < 10s |
| URL → PDF | 30+ QPS | < 15s |
| HTML → 图片 | 150+ QPS | < 2s |
---
## 🤝 贡献
欢迎提交 Issue 和 Pull Request
1. Fork 本仓库
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 提交 Pull Request
---
## 📄 许可证
本项目采用 [MIT 许可证](LICENSE) 开源。
---
## 📞 联系方式
- **Issues**: [GitHub Issues](https://github.com/your-username/html-to-pdf/issues)
- **Discussions**: [GitHub Discussions](https://github.com/your-username/html-to-pdf/discussions)
---
<div align="center">
**如果这个项目对你有帮助,请给一个 ⭐ Star**
Made with ❤️ by the HtmlToPdf Team
</div>