Spring Boot 3.3 + PDFBox 实现电子签章

随着数字化办公和电子合同的普及,PDF 文档已经成为很多业务场景中的标准文件格式。为了确保文档的安全性和法律效力,电子签章技术应运而生。电子签章不仅可以证明文件的真实性,还能防止文件被篡改。在本文中,我们将详细讲解如何使用 Spring Boot 3.3 与 Apache PDFBox 集成,来实现电子签章功能。我们将结合 PDFBox 这一强大的 PDF 处理库,并通过 jQuery + Bootstrap 实现前端文件上传和结果展示的功能。

PDFBox 框架简介

Apache PDFBox 是一个开放源码的 Java 库,专门用于创建、操作和编辑 PDF 文件。它为开发人员提供了丰富的 PDF 操作功能,包括创建 PDF、解析 PDF 文本、操作图像以及表单数据等。PDFBox 的以下特性使其非常适合处理电子签章功能:

  1. 强大的 PDF 文档创建和编辑功能:PDFBox 支持动态创建、读取和编辑 PDF 文档,适用于各种电子文档操作。

  2. 内嵌图像与文本支持:通过 PDFBox,用户可以轻松将图片和文本嵌入到 PDF 文档中的指定位置,这为签章和水印功能提供了便利。

  3. 高效的 PDF 表单解析和填写功能:对于需要生成或修改带有表单域的 PDF 文档,PDFBox 提供了友好的 API,适合在合同或协议等场景中进行签名的自动化处理。

  4. 多平台支持:作为一个基于 Java 的库,PDFBox 可以在多个操作系统上使用,并且与 Spring Boot 集成良好,特别适合用于服务端处理 PDF 文件。

在本文中,我们将使用 PDFBox 的 PDF 编辑功能,结合 Spring Boot 实现电子签章,并通过前端 Thymeleaf + jQuery + Bootstrap 提供用户友好的操作界面,完成整个电子签章流程。

项目依赖配置

首先,我们需要在 pom.xml 中引入所需依赖,包括 Spring BootPDFBox 以及前端模板引擎 Thymeleaf。这些依赖提供了 PDF 操作、Web 服务和前端渲染等功能。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.icoderoad</groupId>
	<artifactId>signature</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>signature</name>
	<description>Demo project for Spring Boot</description>
	
	<properties>
		<java.version>17</java.version>
		<pdfbox.version>3.0.3</pdfbox.version>
	</properties>
	<dependencies>
		
		<!-- Spring Boot Starter Web -->
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-web</artifactId>
	    </dependency>
	    
	    <!-- Spring Boot Starter Thymeleaf -->
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-thymeleaf</artifactId>
	    </dependency>
	
	    <!-- PDFBox for PDF manipulation -->
	    <dependency>
	        <groupId>org.apache.pdfbox</groupId>
	        <artifactId>pdfbox</artifactId>
	        <version>${pdfbox.version}</version>
	    </dependency>
	
	    <!-- Lombok (optional for simplifying Java code) -->
	    <dependency>
	        <groupId>org.projectlombok</groupId>
	        <artifactId>lombok</artifactId>
	        <scope>provided</scope>
	    </dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

配置文件

为方便管理电子签章相关的参数配置,我们可以使用 application.yml 文件存储如签章图片路径、签章字体大小以及签章位置等属性。

server:
  port: 8080

spring:
  servlet:
    multipart:
      max-file-size: 50MB  # 单个文件最大上传大小
      max-request-size: 50MB  # 总的请求最大大小

  
signature:
  upload_dir: /Users/icoderoad/signature/upload/
  image-path: src/main/resources/static/images/signature.png
  font-size: 12
  signature-position-x: 300
  signature-position-y: 620

接下来,创建 SignatureProperties 配置类,使用 @ConfigurationProperties 注解来读取这些配置。

@Data
@Component
@ConfigurationProperties(prefix = "signature")
public class SignatureProperties {
	
    private String uploadDir;
    
    private String imagePath;
    
    private int fontSize;
    
    private float signaturePositionX;
    
    private float signaturePositionY;
    
}

配置类

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private SignatureProperties signatureProperties;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 配置静态资源路径,使得上传文件可以通过 /upload 访问
        String uploadDir = "file:" + signatureProperties.getUploadDir();
        registry.addResourceHandler("/upload/**")
                .addResourceLocations(uploadDir);
    }
}

签章服务实现

PdfSignatureService 是核心业务逻辑类,主要职责是通过 PDFBox 在指定的 PDF 文件上添加签章。通过 PDImageXObject 来加载签章图片,并通过 PDPageContentStream 将图片绘制在 PDF 文件的指定位置。

@Service
public class PdfSignatureService {

    @Autowired
    private SignatureProperties signatureProperties;

    // 实现PDF签章
    public void signPdf(String inputPdfPath, String outputPdfPath) throws IOException {
        File file = new File(inputPdfPath);
        try (PDDocument document = PDDocument.load(file)) {
        	 // 获取签章图片
            PDImageXObject pdImage = PDImageXObject.createFromFile(signatureProperties.getImagePath(), document);
            float imageWidth = 100; // 设置签章图片的宽度
            float imageHeight = 50; // 设置签章图片的高度
            
            // 遍历所有页面
            for (PDPage page : document.getPages()) {
                float pageWidth = page.getMediaBox().getWidth();
                float pageHeight = page.getMediaBox().getHeight();
                
                // 计算签章位置:页面右下角
                float x = pageWidth - imageWidth - 20; // 右边距20
                float y = 70; // 下边距20

                // 为每个页面创建一个新的内容流以添加签名
                try (PDPageContentStream contentStream = new PDPageContentStream(document, page, 
                        PDPageContentStream.AppendMode.APPEND, true, true)) {
                    // 绘制签章图片
                    contentStream.drawImage(pdImage, x, y, imageWidth, imageHeight);
                }
            }
            document.save(outputPdfPath); // 保存修改后的PDF
        }
    }
}

控制器实现

控制器 PdfController 负责处理来自前端的文件上传请求,调用 PdfSignatureService 实现 PDF 文件的签章,并返回结果给前端。

@Controller
public class PdfController {

    @Autowired
    private PdfSignatureService pdfSignatureService;
    
    @Autowired
    private SignatureProperties signatureProperties;
    
    @GetMapping("/")
    public String index() {
        return "index";
    }

    @PostMapping("/uploadAndSignPdf")
    @ResponseBody
    public ResponseEntity<Object> uploadAndSignPdf(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("{\"message\": \"文件上传失败,文件为空\"}");
        }

        try {
            // 保存上传文件到服务器
            File uploadedFile = saveUploadedFile(file);

            // 创建签章后的PDF保存路径
            String signedPdfPath = signatureProperties.getUploadDir() + "signed_" + file.getOriginalFilename();

            // 调用 PdfSignatureService 进行签章
            pdfSignatureService.signPdf(uploadedFile.getAbsolutePath(), signedPdfPath);

            // 返回成功信息给前端
            return ResponseEntity.status(HttpStatus.OK).body("{\"message\": \"文件签章成功\", \"signedPdf\": \"/upload/signed_" + file.getOriginalFilename() + "\"}");
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{\"message\": \"文件处理失败\"}");
        }
    }

    // 保存上传的文件到服务器本地
    private File saveUploadedFile(MultipartFile file) throws IOException {
        File destFile = new File(signatureProperties.getUploadDir() + file.getOriginalFilename());
        try (InputStream inputStream = file.getInputStream();
             FileOutputStream outputStream = new FileOutputStream(destFile)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }
        return destFile;
    }
}

前端实现

前端界面使用 Thymeleaf 渲染,并通过 jQuery 处理文件上传操作。在文件上传成功后,页面将展示签章结果的链接,供用户下载签章后的 PDF 文件。

在 src/main/resources/templates 目录下创建 index.html 文件:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>电子签章</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <div class="container mt-5">
        <h2>上传PDF文件进行签章</h2>
        <form id="uploadForm" enctype="multipart/form-data">
            <div class="mb-3">
                <input type="file" class="form-control" name="file" id="file" required>
            </div>
            <button type="submit" class="btn btn-primary">上传并签章</button>
        </form>
        
        <!-- 显示签章结果 -->
        <div id="resultMessage" class="mt-3" style="display: none;">
            <p id="message"></p>
            <a id="downloadLink" href="#" target="_blank">下载签章后的 PDF</a>
        </div>
    </div>

    <script>
        $(document).ready(function() {
            $('#uploadForm').submit(function(event) {
                event.preventDefault();
                var formData = new FormData();
                formData.append('file', $('#file')[0].files[0]);

                $.ajax({
                    url: '/uploadAndSignPdf',
                    type: 'POST',
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function(jsonResponse) {
                    	response = JSON.parse(jsonResponse); 
                        $('#message').text(response.message);
                        $('#downloadLink').attr('href', response.signedPdf);
                        $('#resultMessage').show();
                    },
                    error: function(xhr) {
                        $('#message').text('签章失败:' + xhr.responseText);
                        $('#resultMessage').show();
                    }
                });
            });
        });
    </script>
</body>
</html>

总结

本文详细讲解了如何通过 Spring Boot 3.3 与 Apache PDFBox 实现电子签章功能,并展示了如何通过 jQuery 实现前端文件上传和结果展示。PDFBox 的强大功能使我们能够灵活地操作 PDF 文档,添加电子签章,保证了文档的安全性和有效性。

来源:https://mp.weixin.qq.com/s/bJTk9e-5UWNDqY9vMYbJXA

 

 

 

 

 

 

 

请登录后发表评论

    没有回复内容