You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue+Spring实现无打印弹窗多打印机指定打印问题求助

无弹窗多模块指定打印机打印问题排查

我在Spring Boot + Vue.js项目中需实现:无打印弹窗打印应用内三个独立模块内容,且每个模块对应不同打印机。因浏览器限制无法在前端实现,故采用后端处理方案:前端使用html2pdf插件将指定HTML元素转换为PDF,以MultipartFile形式发送至后端;后端采用PDFBox的PrinterJob类执行打印操作,但运行时出现Error: End-of-File, expected line错误,提示文件为空,因Java经验不足,无法定位问题,寻求帮助。


前端HTML转PDF代码

let that = this;

html2pdf()
  .set({
    filename: "test.pdf",
    margin: 4,
    jsPDF: {
      orientation: "portrait",
    },
  })
  .from(document.getElementById("voucher-body"))
  .toPdf()
  .output("datauristring")
  .then(function(pdfAsString) {
    const file = new File([pdfAsString], "voucher-body", {
      type: "application/pdf",
      lastModified: new Date().getTime(),
    });

    console.log(file);
    const reader = new FileReader();
    reader.readAsText(file);
    reader.onload = () => console.log(reader.result);

    const formData = new FormData();
    formData.append("data", file);

    that.printForm({
      eventId: that.event.id,
      doctorId: that.doctor.id,
      type: "voucher",
      formFile: formData,
    });
  });

后端Spring处理MultipartFile打印代码

PDDocument document = PDDocument.load(file.getInputStream());
PrinterJob job = PrinterJob.getPrinterJob();
job.setJobName(file.getOriginalFilename());

for (PrintService ps : PrinterJob.lookupPrintServices()) {
    String psName = ps.toString();
    // 选用指定打印机
    if (psName.equals("XP-80C (copy 1)")) {
        job.setPrintService(ps);
        break;
    }
}

job.setPageable(new PDFPageable(document));

Paper paper = new Paper();
// 设置打印纸张大小
paper.setSize(598,842); // 1/72 inch
// 设置打印位置 坐标
paper.setImageableArea(0, 0, paper.getWidth(), paper.getHeight()); // no margins
// custom page format
PageFormat pageFormat = new PageFormat();
pageFormat.setPaper(paper);
// override the page format
Book book = new Book();
// append all pages 设置一些属性 是否缩放 打印张数等
book.append(new PDFPrintable(document, Scaling.ACTUAL_SIZE), pageFormat, 1);
job.setPageable(book);
// 开始打印
job.print();

问题排查与修复方案

前端核心问题:Data URI未转成有效二进制PDF

output("datauristring")返回的是data:application/pdf;base64,...格式的字符串,直接用该字符串创建File对象,本质是存储了文本字符串而非PDF二进制数据,后端解析时自然判定文件为空。

修复后的前端代码:

.then(function(pdfAsString) {
  // 提取base64编码部分,剔除data URI前缀
  const base64Content = pdfAsString.split(',')[1];
  // 将base64解码为二进制数组
  const byteArray = Uint8Array.from(atob(base64Content), char => char.charCodeAt(0));
  // 用二进制数组创建合法的PDF文件对象
  const file = new File([byteArray], "voucher-body.pdf", {
    type: "application/pdf",
    lastModified: new Date().getTime(),
  });

  const formData = new FormData();
  formData.append("pdfFile", file); // 统一参数名,方便后端接收

  that.printForm({
    eventId: that.event.id,
    doctorId: that.doctor.id,
    type: "voucher",
    formFile: formData,
  });
});

后端优化建议

  1. 参数名匹配:确保后端MultipartFile参数名与前端formData.append的key一致(示例中为pdfFile)
  2. 资源释放:打印完成后必须关闭PDDocument,避免内存泄漏,使用try-with-resources自动管理:
try (PDDocument document = PDDocument.load(file.getInputStream())) {
  // 原有打印逻辑
  job.print();
} catch (Exception e) {
  e.printStackTrace();
}
  1. 打印机名称校验:ps.toString()返回的内容可能包含冗余信息,建议先打印所有打印机的真实名称确认:
for (PrintService ps : PrinterJob.lookupPrintServices()) {
  System.out.println("打印机真实名称:" + ps.getName());
}

内容的提问来源于stack exchange,提问作者omar kamal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 03:36:30