HtmlToPdf/docs/外部对接文档.md
2026-03-17 19:52:27 +08:00

16 KiB
Raw Permalink Blame History

HTML 转图片 / PDF 服务 — 外部对接文档

版本2.0.1 | 更新日期2026-03-17


1. 概述

本服务提供 HTML/URL 转图片和 PDF 的能力,支持同步和异步两种调用模式:

  • 同步模式:请求后直接返回文件流,适合简单、低并发场景
  • 异步模式(推荐):提交任务后通过轮询或回调获取结果,适合高并发、大批量场景

服务地址https://pdf-service.example.com(请替换为实际地址)


2. 认证

服务支持 API Key 认证。如果服务端开启了认证,所有业务接口都需要携带 Token。

2.1 获取 Token

POST /api/auth/token
Content-Type: application/json

请求体:

{
  "apiKey": "your-api-key",
  "userId": "your-user-id"
}

响应:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "tokenType": "Bearer",
  "expiresIn": 3600,
  "expiresAt": "2026-03-17T11:00:00Z"
}

2.2 请求认证方式

服务支持两种认证方式,任选其一即可:

方式一JWT Token通过 API Key 换取)

Authorization: Bearer {accessToken}

方式二:直接使用 API Key

X-API-Key: your-api-key

或:

Authorization: ApiKey your-api-key

推荐使用 X-API-Key HeaderQuery String 方式(?api_key=xxx)存在 Key 泄露到日志的风险。

以下路径无需认证:/health/swagger/metrics/api/auth/token

2.3 刷新 Token

POST /api/auth/refresh
Authorization: Bearer {当前Token}

3. 同步转换接口

适合简单场景,请求后直接返回文件二进制流。

3.1 HTML 转图片

POST /api/image/convert/html
Content-Type: application/json

请求体:

{
  "html": "<html><body><h1>Hello</h1></body></html>",
  "options": {
    "format": "png",
    "quality": 90,
    "width": 1920,
    "height": 1080,
    "fullPage": true,
    "omitBackground": false
  },
  "saveLocal": false
}

响应:对应格式的图片文件流(image/pngimage/jpegimage/webp

3.2 URL 转图片

POST /api/image/convert/url
Content-Type: application/json

请求体:

{
  "url": "https://example.com",
  "waitUntil": "networkidle0",
  "timeout": 30000,
  "options": {
    "format": "png",
    "width": 1920,
    "height": 1080,
    "fullPage": true
  },
  "saveLocal": false
}

3.3 HTML 转 PDF

POST /api/pdf/convert/html
Content-Type: application/json

请求体:

{
  "html": "<html><body><h1>Hello World</h1></body></html>",
  "options": {
    "format": "A4",
    "landscape": false,
    "printBackground": true,
    "margin": {
      "top": "10mm",
      "right": "10mm",
      "bottom": "10mm",
      "left": "10mm"
    }
  },
  "saveLocal": false
}

响应application/pdf 文件流

3.4 URL 转 PDF

POST /api/pdf/convert/url
Content-Type: application/json

请求体:

{
  "url": "https://example.com",
  "waitUntil": "networkidle0",
  "timeout": 30000,
  "options": {
    "format": "A4",
    "printBackground": true
  },
  "saveLocal": false
}

4. 异步任务接口(推荐)

异步模式下,提交任务后立即返回任务 ID通过轮询状态或配置回调来获取结果。

4.1 提交图片任务

POST /api/tasks/image
Content-Type: application/json
Idempotency-Key: {可选,幂等键}

请求体:

{
  "source": {
    "type": "html",
    "content": "<h1>Hello World</h1>"
  },
  "options": {
    "format": "png",
    "quality": 90,
    "width": 1920,
    "height": 1080,
    "fullPage": true,
    "omitBackground": false
  },
  "waitUntil": "networkidle2",
  "timeout": 60000,
  "delayAfterLoad": 1000,
  "callback": {
    "url": "https://your-app.com/webhook/image-done",
    "headers": {
      "X-Custom-Header": "value"
    },
    "includeFileData": false
  },
  "saveLocal": true,
  "metadata": {
    "orderId": "12345",
    "source": "your-system"
  }
}

source.type"html" 时,content 填 HTML 字符串;为 "url" 时,content 填页面 URL。 delayAfterLoad:页面加载完成后额外等待的毫秒数,适合有延迟动画的页面。

响应202 Accepted

{
  "taskId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "message": "任务已创建,正在排队处理",
  "createdAt": "2026-03-17T10:00:00Z",
  "estimatedWaitTime": 3,
  "queuePosition": 2,
  "links": {
    "self": "/api/tasks/550e8400-...",
    "status": "/api/tasks/550e8400-.../status",
    "download": "/api/tasks/550e8400-.../download"
  }
}

4.2 提交 PDF 任务

POST /api/tasks/pdf
Content-Type: application/json
Idempotency-Key: {可选}

请求体:

{
  "source": {
    "type": "html",
    "content": "<h1>Hello World</h1>"
  },
  "options": {
    "format": "A4",
    "landscape": false,
    "printBackground": true,
    "margin": {
      "top": "10mm",
      "right": "10mm",
      "bottom": "10mm",
      "left": "10mm"
    }
  },
  "waitUntil": "networkidle0",
  "timeout": 60000,
  "callback": {
    "url": "https://your-app.com/webhook/pdf-done",
    "includeFileData": false
  },
  "saveLocal": true,
  "metadata": {
    "orderId": "12345"
  }
}

4.3 批量提交任务

POST /api/tasks/batch
Content-Type: application/json

请求体:

{
  "tasks": [
    {
      "type": "image",
      "source": { "type": "html", "content": "<h1>截图 1</h1>" },
      "imageOptions": { "format": "png", "width": 1920, "height": 1080 }
    },
    {
      "type": "image",
      "source": { "type": "url", "content": "https://example.com" },
      "imageOptions": { "format": "jpeg", "quality": 85 },
      "waitUntil": "networkidle0"
    },
    {
      "type": "pdf",
      "source": { "type": "html", "content": "<h1>文档</h1>" },
      "pdfOptions": { "format": "A4", "printBackground": true }
    }
  ],
  "callback": {
    "url": "https://your-app.com/webhook/batch-done"
  },
  "onEachComplete": false,
  "onAllComplete": true
}

响应202 Accepted

{
  "batchId": "batch-xxxx-xxxx",
  "taskIds": ["task-1", "task-2", "task-3"],
  "totalTasks": 3,
  "successCount": 3,
  "failedCount": 0,
  "links": {
    "status": "/api/tasks/batch/batch-xxxx-xxxx"
  }
}

4.4 查询任务详情

GET /api/tasks/{taskId}

响应:

{
  "taskId": "550e8400-...",
  "type": "image",
  "source": { "type": "html", "content": "..." },
  "status": "completed",
  "createdAt": "2026-03-17T10:00:00Z",
  "startedAt": "2026-03-17T10:00:01Z",
  "completedAt": "2026-03-17T10:00:03Z",
  "duration": 2000,
  "retryCount": 0,
  "result": {
    "fileSize": 204800,
    "fileType": "png",
    "downloadUrl": "/api/tasks/550e8400-.../download"
  },
  "error": null,
  "links": {
    "self": "/api/tasks/550e8400-...",
    "download": "/api/tasks/550e8400-.../download"
  }
}

4.5 轻量级状态查询

适合高频轮询场景:

GET /api/tasks/{taskId}/status

响应:

{
  "taskId": "550e8400-...",
  "status": "processing",
  "createdAt": "2026-03-17T10:00:00Z",
  "startedAt": "2026-03-17T10:00:01Z",
  "completedAt": null
}

4.6 查询任务列表

GET /api/tasks?status=completed&type=image&page=1&pageSize=20

Query 参数:

参数 类型 说明
status string 按状态筛选pending / processing / completed / failed / cancelled
type string 按类型筛选image / pdf
startDate datetime 创建时间起始
endDate datetime 创建时间截止
page int 页码,默认 1
pageSize int 每页数量,默认 20最大 100

4.7 下载结果文件

GET /api/tasks/{taskId}/download

响应文件二进制流Content-Type 根据任务类型自动设置(image/pngimage/jpegimage/webpapplication/pdf)。

响应头包含:

  • X-Task-Id: 任务 ID
  • X-Expires-At: 文件过期时间(如有)

任务未完成时返回 409 Conflict

4.8 取消任务

DELETE /api/tasks/{taskId}

pending 状态的任务可取消,其他状态返回 409 Conflict

4.9 重试任务

POST /api/tasks/{taskId}/retry

failed 状态的任务可重试,返回 202 Accepted

4.10 查询批量任务状态

GET /api/tasks/batch/{batchId}

4.11 预检Dry-run

提交前检查内容是否可渲染、是否存在 SSRF 风险:

POST /api/tasks/validate
Content-Type: application/json

请求体:

{
  "source": {
    "type": "url",
    "content": "https://example.com"
  }
}

响应:

{
  "ok": true,
  "canRender": true,
  "suggestedQueue": "normal",
  "estimatedRenderTimeMs": 2000,
  "issues": [],
  "ssrfBlocked": false
}

4.12 回调重放

手动重新触发某个任务的回调通知:

POST /api/tasks/{taskId}/callback/replay
Content-Type: application/json

请求体(可选):

{
  "attempts": 3
}

4.13 查询回调日志

GET /api/tasks/{taskId}/callback/logs

响应:

[
  {
    "id": "log-xxx",
    "taskId": "550e8400-...",
    "callbackUrl": "https://your-app.com/webhook",
    "attempt": 1,
    "responseStatus": 200,
    "success": true,
    "errorMessage": null,
    "sentAt": "2026-03-17T10:00:05Z",
    "responseAt": "2026-03-17T10:00:05Z",
    "durationMs": 120,
    "isReplay": false
  }
]

5. 配额查询

5.1 查看当前配额

GET /api/quota

5.2 检查配额是否足够

GET /api/quota/check

响应:

{
  "allowed": true,
  "denyReason": null,
  "dailyRemaining": 950,
  "monthlyRemaining": 9800,
  "concurrentRemaining": 8
}

6. 健康检查

GET /health

7. 回调机制

异步任务支持配置回调 URL任务完成后服务会主动 POST 通知你的系统。

7.1 回调请求格式

POST https://your-app.com/webhook/image-done
Content-Type: application/json
X-Callback-Signature: {HMAC签名}

回调 Body 包含任务完成信息taskId、status、result 等)。如果 includeFileData 设为 true,回调中会包含 Base64 编码的文件数据。

7.2 回调重试

回调失败时,服务会自动进行指数退避重试(最多 3 次)。也可通过 4.12 接口手动重放。


8. 参数参考

8.1 图片选项options

字段 类型 默认值 说明
format string "png" 图片格式:pngjpegwebp
quality int 90 图片质量1-100仅 jpeg/webp 有效
width int 1920 浏览器视口宽度(像素)
height int 1080 浏览器视口高度(像素)
fullPage bool true 是否截取整个页面(含滚动区域)
omitBackground bool false 是否透明背景,仅 png 有效

width/height 直接控制浏览器窗口大小,页面会在该分辨率下渲染后截图。

8.2 PDF 选项options

字段 类型 默认值 说明
format string "A4" 纸张格式A4, Letter, Legal, A3 等
landscape bool false 是否横向
printBackground bool true 是否打印背景色/图
width string 自定义页面宽度(如 "210mm"),设置后忽略 format
height string 自定义页面高度(如 "297mm"),设置后忽略 format
viewportWidth int 浏览器视口宽度(像素)
viewportHeight int 浏览器视口高度(像素)
margin.top/right/bottom/left string "10mm" 页面边距

8.3 waitUntil 参数

说明
load 等待 load 事件触发
domcontentloaded 等待 DOMContentLoaded 事件
networkidle0 等待 500ms 内无网络请求(推荐 SPA 页面)
networkidle2 等待 500ms 内不超过 2 个网络请求(默认)

8.4 任务状态

状态 说明
pending 排队中
processing 处理中
completed 已完成
failed 失败
cancelled 已取消
stalled 卡死(超时未完成)

9. 错误处理

所有错误响应遵循 RFC 7807 ProblemDetails 格式:

{
  "status": 400,
  "title": "请求参数无效",
  "detail": "HTML 内容不能为空"
}
HTTP 状态码 含义 处理建议
400 参数错误 检查请求体格式和必填字段
401 认证失败 检查 Token 或 API Key
404 任务不存在 确认 taskId 是否正确
409 状态冲突 任务状态不允许当前操作
429 请求限流 降低请求频率,稍后重试
503 服务繁忙 队列已满或服务过载,稍后重试

10. .NET SDK 接入

10.1 安装

dotnet add package HtmlToPdfService.Client

10.2 注册服务

builder.Services.AddHtmlToPdfClient(options =>
{
    options.BaseUrl = "https://pdf-service.example.com";
    options.ApiKey = "your-api-key";
    options.TimeoutSeconds = 120;
    options.EnableRetry = true;
    options.RetryCount = 3;
});

10.3 使用示例

// 同步转换 — 直接拿到图片字节数组
public async Task<byte[]> ScreenshotAsync(string html)
{
    return await _client.ConvertHtmlToImageAsync(html, new ImageOptions
    {
        Format = "png",
        Width = 1920,
        Height = 1080,
        FullPage = true
    });
}

// 异步任务 — 提交后等待完成再下载
public async Task<byte[]> ScreenshotAsyncTask(string html)
{
    var result = await _client.SubmitImageTaskAsync(
        source: new SourceInfo { Type = "html", Content = html },
        options: new ImageOptions { Format = "png", Width = 1920 });

    return await _client.WaitAndDownloadAsync(result.TaskId);
}

10.4 SDK 配置项

配置项 类型 默认值 说明
BaseUrl string 服务地址(必填)
ApiKey string null API Key
TimeoutSeconds int 120 HTTP 请求超时
EnableRetry bool false 是否启用自动重试
RetryCount int 3 重试次数

11. 其他语言接入curl 示例)

同步 HTML 转图片

curl -X POST https://pdf-service.example.com/api/image/convert/html \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{"html": "<h1>Hello</h1>", "options": {"format": "png", "width": 1920, "height": 1080}}' \
  -o screenshot.png

异步提交图片任务

curl -X POST https://pdf-service.example.com/api/tasks/image \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "source": {"type": "html", "content": "<h1>Hello</h1>"},
    "options": {"format": "png", "width": 1920, "height": 1080}
  }'

查询状态

curl https://pdf-service.example.com/api/tasks/{taskId}/status \
  -H "X-API-Key: your-api-key"

下载文件

curl https://pdf-service.example.com/api/tasks/{taskId}/download \
  -H "X-API-Key: your-api-key" \
  -o result.png

12. 典型接入流程

方式一:同步调用(简单直接)

客户端 → POST /api/image/convert/html → 等待 → 返回图片文件流

适合:单次截图、低并发、对延迟不敏感。

方式二:异步轮询

客户端 → POST /api/tasks/image → 返回 taskId
客户端 → GET /api/tasks/{taskId}/status → 轮询直到 completed
客户端 → GET /api/tasks/{taskId}/download → 下载图片

适合:需要异步处理但不方便接收回调。

方式三:异步回调(推荐)

客户端 → POST /api/tasks/image带 callback.url→ 返回 taskId
服务端 → 处理完成后 POST 回调到 callback.url
客户端 → 收到回调后 GET /api/tasks/{taskId}/download → 下载图片

适合:高并发、批量处理、生产环境。


13. 注意事项

  1. 幂等性:异步接口支持 Idempotency-Key 请求头,相同 Key 不会重复创建任务
  2. 文件有效期:生成的文件默认保留 7 天,请及时下载
  3. SSRF 防护URL 模式下服务会阻止访问内网地址10.x、192.168.x、127.x 等)
  4. 请求限流:服务有 IP/用户维度的速率限制,超限返回 429
  5. 超时设置:复杂页面建议适当增大 timeout 参数(单位:毫秒)
  6. SPA 页面:转换 React/Vue/Angular 等 SPA 页面时,建议 waitUntil 设为 networkidle0,并配合 delayAfterLoad 等待动画完成