Java實現(xiàn)PDF批量處理之合并、拆分、加水印的技術(shù)方案與實踐
一、技術(shù)選型
Java生態(tài)中處理PDF的庫主要有以下幾個:
| 庫名 | 特點 | 適用場景 |
|------|------|----------|
| Apache PDFBox | 開源免費,功能全面 | 合并、拆分、水印、文本提取 |
| iText | 功能強大,商用需授權(quán) | 復(fù)雜PDF生成、表單處理 |
| OpenPDF | iText的開源分支 | 輕量級PDF生成 |
綜合考慮開源協(xié)議和功能覆蓋度,Apache PDFBox 是大多數(shù)項目的首選。
二、Maven依賴配置
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.27</version> </dependency>
三、PDF合并實現(xiàn)
3.1 核心思路
PDF合并的本質(zhì)是將多個PDF文檔的頁面按順序追加到一個新文檔中。PDFBox提供了PDFMergerUtility工具類來簡化這個過程。
3.2 代碼實現(xiàn)
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import java.io.*;
import java.util.List;
public class PdfMergeService {
/**
* 合并多個PDF文件
* @param inputStreams PDF文件輸入流列表
* @param outputPath 輸出文件路徑
*/
public void mergePdf(List<InputStream> inputStreams, String outputPath)
throws IOException {
PDFMergerUtility merger = new PDFMergerUtility();
merger.setDestinationFileName(outputPath);
for (InputStream is : inputStreams) {
merger.addSource(is);
}
// 執(zhí)行合并,MemoryUsageSetting可控制內(nèi)存使用
merger.mergeDocuments(
org.apache.pdfbox.io.MemoryUsageSetting.setupMainMemoryOnly()
);
}
}
3.3 注意事項
- 內(nèi)存管理:處理大文件時建議使用
setupTempFileOnly()將臨時數(shù)據(jù)寫入磁盤,避免OOM - 文件順序:合并順序取決于
addSource的調(diào)用順序,前端可通過拖拽排序來控制 - 書簽處理:合并后原有書簽可能丟失,如需保留需額外處理
PDDocumentOutline
四、PDF拆分實現(xiàn)
4.1 按頁碼范圍拆分
import org.apache.pdfbox.multipdf.Splitter;
import org.apache.pdfbox.pdmodel.PDDocument;
public class PdfSplitService {
/**
* 按頁碼范圍拆分PDF
* @param inputStream 源PDF輸入流
* @param startPage 起始頁(從1開始)
* @param endPage 結(jié)束頁
*/
public byte[] splitByPageRange(InputStream inputStream,
int startPage, int endPage) throws IOException {
try (PDDocument document = PDDocument.load(inputStream);
PDDocument newDoc = new PDDocument()) {
int totalPages = document.getNumberOfPages();
// 參數(shù)校驗
startPage = Math.max(1, startPage);
endPage = Math.min(totalPages, endPage);
for (int i = startPage - 1; i < endPage; i++) {
newDoc.addPage(document.getPage(i));
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
newDoc.save(baos);
return baos.toByteArray();
}
}
}
4.2 性能優(yōu)化建議
對于頁數(shù)很多的PDF(如上千頁),逐頁操作可能較慢??梢钥紤]:
- 使用
Splitter類的setSplitAtPage()方法按固定頁數(shù)批量拆分 - 對于僅需提取少量頁面的場景,直接操作頁面樹比使用Splitter更高效
五、PDF加水印實現(xiàn)
5.1 文字水印核心代碼
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.util.Matrix;
public class PdfWatermarkService {
public byte[] addTextWatermark(InputStream input, String text,
float opacity, float rotation, float fontSize) throws IOException {
try (PDDocument document = PDDocument.load(input)) {
// 遍歷每一頁添加水印
for (PDPage page : document.getPages()) {
PDPageContentStream cs = new PDPageContentStream(
document, page,
PDPageContentStream.AppendMode.APPEND, true, true
);
// 設(shè)置透明度
PDExtendedGraphicsState gs = new PDExtendedGraphicsState();
gs.setNonStrokingAlphaConstant(opacity);
cs.setGraphicsStateParameters(gs);
// 設(shè)置字體和顏色
cs.setFont(PDType1Font.HELVETICA_BOLD, fontSize);
cs.setNonStrokingColor(200, 200, 200); // 淺灰色
// 計算頁面中心位置
float pageWidth = page.getMediaBox().getWidth();
float pageHeight = page.getMediaBox().getHeight();
float cx = pageWidth / 2;
float cy = pageHeight / 2;
// 旋轉(zhuǎn)變換
cs.beginText();
Matrix matrix = Matrix.getRotateInstance(
Math.toRadians(rotation), cx, cy
);
cs.setTextMatrix(matrix);
cs.showText(text);
cs.endText();
cs.close();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
return baos.toByteArray();
}
}
}
5.2 中文水印的坑
PDFBox默認的PDType1Font不支持中文字符,直接使用會導(dǎo)致亂碼或報錯。解決方案:
// 加載系統(tǒng)中文字體
PDType0Font chineseFont = PDType0Font.load(document,
new FileInputStream("/usr/share/fonts/wqy-microhei/wqy-microhei.ttc"), 0);
cs.setFont(chineseFont, fontSize);
服務(wù)器部署時需確保安裝了中文字體:
# CentOS/RHEL sudo yum install -y wqy-microhei-fonts # Ubuntu/Debian sudo apt-get install -y fonts-wqy-microhei
六、封裝為REST API
在Spring Boot項目中,可以將上述功能封裝為統(tǒng)一的REST接口:
@RestController
@RequestMapping("/api/document/pdf")
public class PdfController {
@PostMapping("/merge")
public ResponseEntity<?> merge(
@RequestParam("files") MultipartFile[] files) {
// 調(diào)用合并服務(wù)
}
@PostMapping("/split")
public ResponseEntity<?> split(
@RequestParam("file") MultipartFile file,
@RequestParam("startPage") int startPage,
@RequestParam("endPage") int endPage) {
// 調(diào)用拆分服務(wù)
}
@PostMapping("/watermark")
public ResponseEntity<?> watermark(
@RequestParam("file") MultipartFile file,
@RequestParam("text") String text,
@RequestParam(defaultValue = "0.5") float opacity) {
// 調(diào)用水印服務(wù)
}
}
七、實際效果
如果你不想自己搭建服務(wù),也可以直接使用現(xiàn)成的在線工具來處理PDF。比如 輕語API開放平臺 提供了免費的 PDF在線處理工具,支持合并、拆分、加水印、轉(zhuǎn)Word、轉(zhuǎn)圖片等功能,底層就是基于上述技術(shù)方案實現(xiàn)的。
對于需要批量處理的場景,也可以通過API接口集成到自己的系統(tǒng)中,省去重復(fù)造輪子的成本。
八、總結(jié)
| 功能 | 核心類/方法 | 難點 |
|------|------------|------|
| PDF合并 | PDFMergerUtility | 大文件內(nèi)存管理 |
| PDF拆分 | PDDocument.getPage() | 頁碼邊界校驗 |
| PDF水印 | PDPageContentStream | 中文字體支持 |
PDF處理看似簡單,但在生產(chǎn)環(huán)境中需要關(guān)注內(nèi)存控制、字體兼容、并發(fā)處理等問題。希望本文的實踐經(jīng)驗?zāi)軒椭闵俨纫恍┛印?/p>
以上就是Java實現(xiàn)PDF批量處理之合并、拆分、加水印的技術(shù)方案與實踐的詳細內(nèi)容,更多關(guān)于Java批量處理PDF文檔的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Graceful Response 構(gòu)建 Spring Boot 響應(yīng)
Graceful Response是一個Spring Boot技術(shù)棧下的優(yōu)雅響應(yīng)處理器,提供一站式統(tǒng)一返回值封裝、全局異常處理、自定義異常錯誤碼等功能,本文介紹Graceful Response 構(gòu)建 Spring Boot 下優(yōu)雅的響應(yīng)處理,感興趣的朋友一起看看吧2024-01-01
IDEA 中創(chuàng)建SpringBoot 父子模塊的實現(xiàn)
這篇文章主要介紹了IDEA 中創(chuàng)建SpringBoot 父子模塊的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
詳解spring mvc4使用及json 日期轉(zhuǎn)換解決方案
本篇文章主要介紹了spring mvc4使用及json 日期轉(zhuǎn)換解決方案,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01

