在 Web 应用中,直接通过浏览器调用打印机存在诸多限制(如跨浏览器兼容性、权限控制、高级功能支持等)。而 WebSocket 技术通过建立客户端与服务端的实时通信通道,能够突破这些限制,实现灵活、可控的打印功能。本文将详细介绍如何通过 WebSocket 架构实现浏览器调用打印机的完整方案。
一、方案整体架构
基于 WebSocket 的打印方案需要前端、后端服务、打印服务三者协同工作,核心架构如下:
|
浏览器(前端) <–WebSocket–> 后端服务 <–本地接口–> 打印机 |
前端:负责收集打印数据、发起打印请求、展示打印状态WebSocket 服务:建立长连接,实现前后端实时通信后端服务:处理打印任务队列、权限验证、调用系统打印接口打印服务:与操作系统交互,直接控制打印机硬件
该架构的优势在于:
突破浏览器沙箱限制,实现对打印机的直接控制支持跨浏览器(Chrome、Firefox、Edge 等均支持 WebSocket)可实现高级打印功能(指定打印机、设置参数、批量打印等)便于集中管理打印任务和权限控制
二、技术栈选择
1. 前端技术
原生 JavaScript(或框架如 Vue/React)处理 WebSocket 通信HTML/CSS 构建打印预览界面JSON 格式封装打印数据
2. 后端技术
WebSocket 服务:Node.js(ws 库)、Java(Netty)、Python(WebSocket-server)等打印接口:根据操作系统选择相应库
Windows:Win32 API、PyWin32Linux:CUPS(Common Unix Printing System)macOS:CUPS 或 AppleScript
3. 通信协议
采用 JSON 格式封装消息,结构示例:
|
{ “type”: “print”, // 消息类型:print/state/cancel “taskId”: “12345”, // 任务ID “data”: { // 打印数据 “content”: “<h1>打印内容</h1>”, // HTML内容或文本 “printerName”: “HP-LaserJet”, // 目标打印机 “copies”: 1, // 份数 “orientation”: “portrait” // 方向:portrait/landscape }, “timestamp”: 1629260000000 } |
三、实现步骤(以 Node.js 为例)
1. 搭建 WebSocket 服务端
使用 Node.js 的ws库创建 WebSocket 服务,处理客户端连接和打印请求:
|
const WebSocket = require('ws'); const { handlePrintTask } = require('./printService'); // 打印处理模块 // 创建WebSocket服务器 const wss = new WebSocket.Server({ port: 8080 }); // 监听连接 wss.on('connection', (ws) => { console.log('客户端已连接'); // 接收客户端消息 ws.on('message', (message) => { const data = JSON.parse(message.toString());
// 处理打印请求 if (data.type === 'print') { const taskId = `task-${Date.now()}`;
// 发送任务受理回执 ws.send(JSON.stringify({ type: 'taskAccepted', taskId, status: 'pending' }));
// 处理打印任务(异步) handlePrintTask({ …data.data, taskId }).then(result => { // 发送打印结果 ws.send(JSON.stringify({ type: 'printResult', taskId, status: result.success ? 'completed' : 'failed', message: result.message })); }); } }); // 监听断开连接 ws.on('close', () => { console.log('客户端已断开'); }); }); console.log('WebSocket打印服务已启动,端口:8080'); |
2. 实现打印服务模块
根据操作系统实现具体打印逻辑,以下是 Windows 系统的示例(使用pdf-to-printer库打印 PDF 文件):
|
const fs = require('fs'); const path = require('path'); const { print } = require('pdf-to-printer'); const { JSDOM } = require('jsdom'); const PDFDocument = require('pdfkit'); // 处理打印任务 async function handlePrintTask(task) { try { // 1. 将HTML内容转换为PDF(便于精确控制打印格式) const pdfPath = await htmlToPdf(task.content);
// 2. 调用打印机 const printerOptions = { printer: task.printerName, pages: '1', // 打印页码范围 copies: task.copies.toString() };
await print(pdfPath, printerOptions);
// 3. 清理临时文件 fs.unlinkSync(pdfPath);
return { success: true, message: `打印成功,任务ID:${task.taskId}` }; } catch (error) { return { success: false, message: `打印失败:${error.message}` }; } } // HTML转PDF工具函数 function htmlToPdf(htmlContent) { return new Promise((resolve, reject) => { const tempPath = path.join(os.tmpdir(), `print-${Date.now()}.pdf`); const doc = new PDFDocument(); const stream = fs.createWriteStream(tempPath);
doc.pipe(stream);
// 解析HTML并添加到PDF(简化示例) const dom = new JSDOM(htmlContent); const textContent = dom.window.document.body.textContent; doc.text(textContent, 50, 50);
doc.end();
stream.on('finish', () => resolve(tempPath)); stream.on('error', reject); }); } module.exports = { handlePrintTask }; |
3. 前端实现(浏览器端)
前端需要实现 WebSocket 连接、打印数据处理、状态展示等功能:
|
<!DOCTYPE html> <html> <head> <title>WebSocket打印示例</title> <style> .print-preview { border: 1px solid #ccc; padding: 20px; margin: 20px 0; } .status { color: #666; margin: 10px 0; } </style> </head> <body> <h1>打印控制台</h1> <div class=”print-preview”> <h2>测试打印内容</h2> <p>这是通过WebSocket发送的打印测试页</p> <p>打印时间:<span></span></p> </div> <select> <!– 打印机列表将通过WebSocket获取 –> </select> <button onclick=”sendPrintRequest()”>打印</button> <div class=”status”></div> <script> // 连接WebSocket服务 const ws = new WebSocket('ws://localhost:8080'); let currentTaskId = null; // 初始化 ws.onopen = () => { setStatus('已连接到打印服务'); // 请求打印机列表 ws.send(JSON.stringify({ type: 'getPrinters' })); }; // 接收消息 ws.onmessage = (event) => { const data = JSON.parse(event.data); handleServerMessage(data); }; // 处理服务端消息 function handleServerMessage(data) { switch (data.type) { case 'printersList': // 渲染打印机列表 const select = document.getElementById('printerSelect'); data.printers.forEach(printer => { const option = document.createElement('option'); option.value = printer.name; option.textContent = printer.name; select.appendChild(option); }); break;
case 'taskAccepted': setStatus(`任务已受理,ID:${data.taskId}`); currentTaskId = data.taskId; break;
case 'printResult': setStatus(`任务${data.taskId}:${data.status} – ${data.message}`); break; } } // 发送打印请求 function sendPrintRequest() { const printContent = document.getElementById('preview').innerHTML; const printerName = document.getElementById('printerSelect').value;
document.getElementById('printTime').textContent = new Date().toLocaleString();
ws.send(JSON.stringify({ type: 'print', data: { content: printContent, printerName: printerName, copies: 1, orientation: 'portrait' } })); } // 状态显示 function setStatus(text) { document.getElementById('status').textContent = text; } // 错误处理 ws.onerror = (error) => { setStatus(`连接错误:${error.message}`); }; // 关闭处理 ws.onclose = () => { setStatus('打印服务已断开连接'); }; </script> </body> </html> |
4. 打印机列表获取(服务端扩展)
服务端需要提供获取可用打印机列表的功能(以 Windows 为例):
|
// 在printService.js中添加 const { exec } = require('child_process'); // 获取Windows系统打印机列表 function getPrinters() { return new Promise((resolve, reject) => { // 通过wmic命令获取打印机信息 exec('wmic printer get name', (error, stdout) => { if (error) { reject(error); return; }
// 解析输出结果 const printers = stdout .split(' .map(line => line.trim()) .filter(line => line && line !== 'Name') // 过滤标题和空行 .map(name => ({ name }));
resolve(printers); }); }); } // 在WebSocket服务中添加处理 ws.on('message', (message) => { const data = JSON.parse(message.toString()); if (data.type === 'getPrinters') { getPrinters().then(printers => { ws.send(JSON.stringify({ type: 'printersList', printers })); }); } // … 其他消息处理 }); |
四、高级功能扩展
1. 打印任务队列管理
服务端实现任务队列,支持任务优先级、取消、重试前端实时展示队列状态和进度
2. 权限控制
在 WebSocket 连接时验证用户身份(如 JWT 令牌)限制用户可使用的打印机和打印份数
3. 批量打印
前端支持选择多个文件 / 数据服务端实现并发控制,避免打印机过载
4. 打印模板管理
前端提供模板编辑功能服务端存储模板,支持动态填充数据
五、部署与安全考量
1. 部署方案
WebSocket 服务与 Web 应用可部署在同一服务器生产环境需使用 wss://(WebSocket Secure)加密通信考虑使用 Nginx 反向代理 WebSocket 连接
2. 安全措施
实现 WebSocket 连接认证(如 Token 验证)限制打印内容大小,防止恶意请求对打印数据进行 sanitize 处理,防止 XSS 攻击定期清理临时打印文件
六、与传统方案对比
|
方案 |
优势 |
劣势 |
适用场景 |
|
原生 window.print () |
简单直接,无需额外服务 |
无法指定打印机,依赖浏览器预览 |
简单文档打印 |
|
OCX/ActiveX |
可直接调用系统打印机 |
仅支持 IE,安全风险高 |
老旧 Windows 系统 |
|
WebSocket + 后端服务 |
跨浏览器、功能完整、安全可控 |
需要后端开发,部署复杂 |
企业级应用、复杂打印需求 |
七、总结
通过 WebSocket 实现浏览器调用打印机的方案,本质是利用 WebSocket 的实时通信能力,将前端的打印请求传递给后端服务,再由后端服务与操作系统交互完成打印操作。该方案突破了浏览器的限制,同时保持了 Web 应用的跨平台优势,特别适合需要精确控制打印过程的企业级应用。
在实际开发中,需根据具体需求选择合适的技术栈,重点关注打印格式一致性、任务管理和安全性,以实现稳定、高效的打印功能!





