综合档案管理系统,别再让资料乱成一锅粥了
你有没有发现,很多单位,甭管大小,一提档案管理就头疼。纸质文件堆成山,找个去年的合同得翻半天;电子文件倒是方便,可U盘、电脑、网盘到处存,版本对不上是常事,一不小心还误删了。这事儿吧,说白了就是缺一套...
2026年06月17日 03:05:20
在综合档案管理系统中,打印功能通常要求高精度、支持套打以及复杂的版式设计。为了实现前端所见即所得且后端可归档的打印方案,本文采用Java后端生成PDF + 前端预览下载的模式。核心工具选用iText7的html2pdf组件,它能够直接将HTML/CSS转换为PDF,极大降低了排版难度。
在Maven项目的pom.xml中引入必要的依赖包。请确保版本号一致,避免因版本不兼容导致的类找不到异常。
com.itextpdf
html2pdf
4.0.5
com.itextpdf
font-asian
7.2.5
在档案打印中,最常见的问题是生成的PDF中文显示为方块或乱码。这是因为HTML转PDF时默认没有加载中文字体。我们需要创建一个字体提供程序,指定系统字体或项目资源目录下的字体文件。
请在项目的src/main/resources/fonts目录下放入一个中文字体文件,例如SimSun.ttf(宋体)。以下是字体工具类的完整代码:

package com.archives.print.util;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.FontProgramFactory;
import com.itextpdf.layout.font.FontProvider;
import java.io.IOException;
public class CustomFontProvider extends FontProvider {
public CustomFontProvider() {
try {
// 1. 注册宋体,用于正文
FontProgram fontProgram = FontProgramFactory.createFont("src/main/resources/fonts/SimSun.ttf");
this.addFont(fontProgram);
// 2. 注册黑体,用于标题(可选)
// FontProgram simHei = FontProgramFactory.createFont("src/main/resources/fonts/SimHei.ttf");
// this.addFont(simHei);
System.out.println("字体加载成功");
} catch (IOException e) {
System.err.println("字体加载失败,请检查文件路径:" + e.getMessage());
throw new RuntimeException("字体初始化异常", e);
}
}
}
为了方便维护,我们将打印模板设计为HTML字符串。在实际开发中,建议使用FreeMarker或Thymeleaf模板引擎,这里为了演示零门槛操作,直接使用Java字符串拼接。模板中使用了内联CSS来控制A4纸的边距和打印样式。
public class PrintTemplate {
public static String getArchiveCover(String archiveId, String title, String date, String secret) {
return "" +
"" +
"" +
"" +
"" +
"" +
"" +
" 档案案卷封面" +
" " +
" 案卷题名:" +
" " + title + "" +
" " +
" " +
" 档号:" +
" " + archiveId + "" +
" " +
" " +
" 归档日期:" +
" " + date + "" +
" " +
" " +
" 密级:" +
" " + secret + "" +
" " +
"" +
"";
}
}
这是打印管理的核心部分。我们需要编写一个服务类,将HTML源码转换为PDF字节数组。该类会调用前面配置的CustomFontProvider来解决中文乱码问题。
package com.archives.print.service;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.archives.print.util.CustomFontProvider;
import org.springframework.stereotype.Service;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@Service
public class ArchivePrintService {
/
将HTML内容转换为PDF字节数组
@param htmlContent HTML源码
@return PDF文件的二进制数据
/
public byte[] generatePdfBytes(String htmlContent) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
// 1. 创建转换属性配置
ConverterProperties properties = new ConverterProperties();
// 2. 设置自定义字体提供器(关键步骤)
properties.setFontProvider(new CustomFontProvider());
// 3. 设置BaseUri,用于解析HTML中的相对路径资源(如图片),这里设为空
properties.setBaseUri("");
// 4. 执行转换
HtmlConverter.convertToPdf(htmlContent, outputStream, properties);
return outputStream.toByteArray();
} catch (IOException e) {
System.err.println("PDF生成失败:" + e.getMessage());
throw new RuntimeException("打印服务异常", e);
} finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
既然是“打印管理”,除了生成文件,还需要记录谁在什么时候打印了什么档案。这里展示一个简单的业务逻辑,包含模拟的数据库保存操作。
package com.archives.print.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class PrintLogService {
@Autowired
private ArchivePrintService archivePrintService;
// 模拟的日志记录方法
private void saveLog(String userId, String archiveId) {
// TODO: 将以下信息插入到数据库表 t_print_log
System.out.println("记录打印日志 -> 用户: " + userId + ", 档案ID: " + archiveId + ", 时间: " + new Date());
}
/
综合处理打印业务:生成PDF并记录日志
/
public byte[] handleArchivePrint(String userId, String archiveId, String title, String date, String secret) {
// 1. 生成HTML
String html = PrintTemplate.getArchiveCover(archiveId, title, date, secret);
// 2. 转换PDF
byte[] pdfBytes = archive.generatePdfBytes(html);
// 3. 记录日志
saveLog(userId, archiveId);
return pdfBytes;
}
}
我们需要暴露一个HTTP接口供前端调用。该接口接收档案参数,返回PDF文件流,浏览器会自动触发下载或预览。
package com.archives.print.controller;
import com.archives.print.service.PrintLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.;
@RestController
@RequestMapping("/api/print")
public class PrintController {
@Autowired
private PrintLogService printLogService;
@PostMapping("/archive")
public ResponseEntity printArchiveCover(
@RequestParam String userId,
@RequestParam String archiveId,
@RequestParam String title,
@RequestParam String date,
@RequestParam String secret) {
try {
// 调用业务层获取PDF数据
byte[] pdfContents = printLogService.handleArchivePrint(userId, archiveId, title, date, secret);
// 设置响应头,指定文件名和内容类型
String filename = "Archive_" + archiveId + ".pdf";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
headers.setContentDispositionFormData("attachment", filename); // attachment表示下载,inline表示预览
headers.setContentLength(pdfContents.length);
return new ResponseEntity<>(pdfContents, headers, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
后端完成后,前端无需安装任何插件,直接通过请求接口即可实现打印。以下是一个纯HTML/JS的测试页面代码:
档案打印测试
档案打印管理测试
CustomFontProvider中的字体路径在服务器上是绝对存在的,否则会抛出异常。ConverterProperties.setBaseUri必须设置为图片可访问的HTTP地址或本地文件路径前缀。FontProvider配置为单例Bean,避免每次请求都重新读取字体文件。