Java应用开发:监听打印队列并按文档属性重定向作业
Java监听打印队列并获取作业文档属性的解决方案
核心问题说明
JDK自带的javax.print API仅能获取打印作业的基础状态(如队列计数、作业ID),无法直接获取文档名称、内容等关键属性,必须依赖系统原生打印服务API或第三方库实现。以下分平台给出具体实现方案:
Windows平台实现
通过JNA调用Windows打印池(Winspool.drv)的原生API,可直接获取作业的文档名称、内容等属性。
1. 依赖准备
添加JNA及JNA平台依赖(Maven示例):
<dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>5.13.0</version> </dependency> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna-platform</artifactId> <version>5.13.0</version> </dependency>
2. 调用打印池API获取文档名称
定义JNA接口映射Winspool函数,并通过JOB_INFO_2结构体解析作业属性:
import com.sun.jna.Native; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.ptr.IntByReference; import com.sun.jna.Structure; import com.sun.jna.win32.StdCallLibrary; import java.util.Arrays; import java.util.List; public interface WinSpool extends StdCallLibrary { WinSpool INSTANCE = Native.load("winspool.drv", WinSpool.class); boolean OpenPrinter(String pPrinterName, WinNT.HANDLEByReference phPrinter, int pDefault); boolean EnumJobs(WinNT.HANDLE hPrinter, int firstJob, int noJobs, int level, Structure[] jobs, int cbBuf, IntByReference pcbNeeded, IntByReference pcReturned); boolean ClosePrinter(WinNT.HANDLE hPrinter); class JOB_INFO_2 extends Structure { public int JobId; public String pPrinterName; public String pMachineName; public String pUserName; public String pDocument; // 文档名称字段 @Override protected List<String> getFieldOrder() { return Arrays.asList("JobId", "pPrinterName", "pMachineName", "pUserName", "pDocument"); } } } // 使用示例 public class WindowsPrintMonitor { public static void main(String[] args) { String targetPrinter = "printspoller"; WinNT.HANDLEByReference printerHandle = new WinNT.HANDLEByReference(); if (WinSpool.INSTANCE.OpenPrinter(targetPrinter, printerHandle, 0)) { IntByReference pcbNeeded = new IntByReference(); IntByReference pcReturned = new IntByReference(); // 先获取所需缓冲区大小 WinSpool.INSTANCE.EnumJobs(printerHandle.getValue(), 0, 100, 2, null, 0, pcbNeeded, pcReturned); int jobCount = pcReturned.getValue(); WinSpool.JOB_INFO_2[] jobInfos = new WinSpool.JOB_INFO_2[jobCount]; // 填充作业数据 if (WinSpool.INSTANCE.EnumJobs(printerHandle.getValue(), 0, jobCount, 2, jobInfos, pcbNeeded.getValue(), pcbNeeded, pcReturned)) { for (WinSpool.JOB_INFO_2 job : jobInfos) { System.out.printf("作业ID: %d | 文档名称: %s%n", job.JobId, job.pDocument); } } WinSpool.INSTANCE.ClosePrinter(printerHandle.getValue()); } } }
3. 获取文档内容
调用ReadPrinter函数可读取作业的原始数据流(RAW/EMF格式),需先通过OpenPrinter获取打印机句柄,再结合作业ID调用该函数。由于打印机型号一致,可直接将数据流转发至目标打印机,无需解析;若需解析内容,需针对数据流格式(如EMF、PDF)使用对应解析库。
Linux/Unix平台实现
基于CUPS(通用UNIX打印系统),可通过命令行工具或CUPS API获取作业属性。
1. 命令行方式快速实现
通过执行系统命令解析打印队列信息:
import java.io.BufferedReader; import java.io.InputStreamReader; public class LinuxPrintMonitor { public static void main(String[] args) throws Exception { // 列出目标打印机的所有作业 Process process = Runtime.getRuntime().exec("lpstat -o printspoller"); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { // 解析输出格式:<作业ID>-<用户名> <文档名称> <大小> <时间> String[] parts = line.split("\\s+"); String jobId = parts[0].split("-")[0]; String docName = parts[1]; System.out.printf("作业ID: %s | 文档名称: %s%n", jobId, docName); } // 导出作业内容(需root权限或CUPS配置允许) // Runtime.getRuntime().exec(String.format("lp -i %s -o raw > job-content.prn", jobId)); } }
2. CUPS API方式
使用cups4j库直接调用CUPS的HTTP API,可更灵活地获取作业属性及内容(需添加对应依赖)。
作业重定向逻辑
获取文档名称/内容后,可按以下步骤实现重定向:
- 读取作业原始数据流(Windows用
ReadPrinter,Linux用CUPS导出) - 基于文档名称或内容匹配目标打印机
- 使用
javax.printAPI将数据流提交至目标打印机(因打印机型号一致,无需转换格式)
内容的提问来源于stack exchange,提问作者Hannes Ledl
相关产品推荐
相关产品推荐

