接口地址
从 ZeroPrint 客户端复制
请求打印
POST /api/print
Content-Type: application/json
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
target | string | 是 | 目标打印机 ID,可从客户端复制 |
data | string | 是 | 打印任务数据。明文 JSON(仅调试)或 RSA+AES-256-GCM 加密后 Base64 编码(生产推荐) |
请求示例
明文模式(仅调试)
{
"target": "<打印机 ID>",
"data": "{\"url\":\"https://example.com/document.pdf\",\"copies\":1,\"id\":\"order_123\",\"callbackUrl\":\"https://your-server.com/callback\"}"
}
加密模式(生产推荐)
{
"target": "<打印机 ID>",
"data": "SGVsbG8gV29ybGQgZm9yIGVuY3J5cHRlZC9pbnRlcmFjdGlvbiBvbmx5..."
}
data 字段参数
pageSize、duplex、color、orientation、resolution需从客户端复制打印机支持的参数值,不提供则使用打印机默认设置。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
url | string | 是 | 待打印文件的 URL,支持公网和内网地址 |
copies | number | 否 | 打印份数,默认 1 |
id | string | 否 | 任务 ID,用于回调关联 |
callbackUrl | string | 否 | 打印结果回调地址 |
pageSize | string | 否 | 纸张尺寸(从客户端复制) |
duplex | string | 否 | 双面模式(从客户端复制) |
color | string | 否 | 颜色模式(从客户端复制) |
orientation | string | 否 | 页面方向(从客户端复制) |
resolution | string | 否 | 打印分辨率(从客户端复制) |
响应
成功:HTTP 200 OK,无响应体。
失败:HTTP 400 Bad Request
{
"code": "PRINTER_OFFLINE",
"message": "target printer is offline"
}
| code | message |
|---|---|
PRINTER_OFFLINE | target printer is offline |
INVALID_TARGET | target is required and must be a 64-character lowercase hex string |
INVALID_DATA | data is required and must be a non-empty string |
INVALID_REQUEST | Invalid JSON format |
回调通知
打印任务完成后,客户端会向 callbackUrl 发送 POST 请求通知结果。需要同时提供 id 和 callbackUrl 才会触发回调。
POST {callbackUrl}
Content-Type: application/json
回调参数
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 任务 ID(与请求中的 id 一致) |
success | boolean | 是否打印成功 |
error | string | 错误描述(仅失败时) |
成功回调
{ "id": "order_123", "success": true }
失败回调
{ "id": "order_123", "success": false, "error": "PDF printing failed: ..." }
回调超时 5 秒。HTTP 状态码 2xx 视为成功。不论打印成功或失败都会执行回调。
加密方案
生产环境推荐 RSA-2048 + AES-256-GCM 混合加密。私钥只保存在客户端本地,云端无法解密。
加密流程
- 复制公钥(来自客户端)
- 生成 AES-256 密钥和 12 字节 Nonce
- AES-256-GCM 加密 JSON(含认证标签)
- RSA-OAEP(SHA256) 加密 AES 密钥(256 字节)
- 拼接:加密密钥 + Nonce + 密文 + 标签
- Base64 编码后作为 data 值
数据结构
[RSA加密的AES密钥 256字节][GCM Nonce 12字节][密文 + 认证标签]
示例代码
const crypto = require("crypto");
// RSA+AES-256-GCM 混合加密:用公钥加密明文,返回 Base64 密文
function encryptData(publicKey, plaintext) {
const wrapped = publicKey.match(/.{1,64}/g).join("\n");
const pemKey = `-----BEGIN PUBLIC KEY-----\n${wrapped}\n-----END PUBLIC KEY-----`;
const aesKey = crypto.randomBytes(32);
const nonce = crypto.randomBytes(12);
const cipher = crypto.createCipheriv("aes-256-gcm", aesKey, nonce);
const ciphertext = Buffer.concat([
cipher.update(plaintext, "utf8"),
cipher.final(),
]);
const tag = cipher.getAuthTag();
const encryptedAesKey = crypto.publicEncrypt(
{
key: pemKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
},
aesKey,
);
return Buffer.concat([encryptedAesKey, nonce, ciphertext, tag]).toString("base64");
}
// 发送打印请求(publicKey 为空时使用明文模式)
async function sendPrintRequest(target, printData, publicKey) {
const plaintext = JSON.stringify(printData);
const data = publicKey ? encryptData(publicKey, plaintext) : plaintext;
const response = await fetch("https://<接口地址>/api/print", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ target, data }),
});
if (!response.ok) throw await response.json();
return { status: "success" };
}
const target = "<打印机 ID>";
const publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAO...";
const printData = {
url: "https://example.com/document.pdf",
copies: 1,
id: "order_123",
callbackUrl: "https://your-server.com/callback",
};
sendPrintRequest(target, printData, publicKey)
.then(() => console.log("加密模式 - 请求成功"))
.catch((error) => console.error("请求失败:", error));
sendPrintRequest(target, printData)
.then(() => console.log("明文模式 - 请求成功"))
.catch((error) => console.error("请求失败:", error));
// RSA+AES-256-GCM 混合加密(浏览器版)
async function encryptData(publicKey, plaintext) {
const publicKeyBytes = Uint8Array.from(atob(publicKey), c => c.charCodeAt(0));
const publicKeyObj = await crypto.subtle.importKey(
"spki", publicKeyBytes,
{ name: "RSA-OAEP", hash: "SHA-256" }, true, ["encrypt"]
);
const aesKey = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 }, true, ["encrypt"]
);
const nonce = crypto.getRandomValues(new Uint8Array(12));
const data = new TextEncoder().encode(plaintext);
const ciphertext = new Uint8Array(
await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, aesKey, data)
);
const aesKeyBytes = new Uint8Array(await crypto.subtle.exportKey("raw", aesKey));
const encryptedAesKey = new Uint8Array(
await crypto.subtle.encrypt(
{ name: "RSA-OAEP", hash: "SHA-256" }, publicKeyObj, aesKeyBytes
)
);
const combined = new Uint8Array(encryptedAesKey.length + nonce.length + ciphertext.length);
combined.set(encryptedAesKey, 0);
combined.set(nonce, encryptedAesKey.length);
combined.set(ciphertext, encryptedAesKey.length + nonce.length);
return btoa(String.fromCharCode(...combined));
}
// 发送打印请求(浏览器版,publicKey 为空时使用明文模式)
async function sendPrintRequest(target, printData, publicKey) {
const plaintext = JSON.stringify(printData);
const data = publicKey ? await encryptData(publicKey, plaintext) : plaintext;
const response = await fetch("https://<接口地址>/api/print", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ target, data }),
});
if (!response.ok) throw await response.json();
return { status: "success" };
}
const target = "<打印机 ID>";
const publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAO...";
const printData = {
url: "https://example.com/document.pdf",
copies: 1,
id: "order_123",
callbackUrl: "https://your-server.com/callback",
};
sendPrintRequest(target, printData, publicKey)
.then(() => console.log("加密模式 - 请求成功"))
.catch((error) => console.error("请求失败:", error));
sendPrintRequest(target, printData)
.then(() => console.log("明文模式 - 请求成功"))
.catch((error) => console.error("请求失败:", error));
其他语言可参照此逻辑自行实现,或使用 AI 辅助翻译。