1. 程式人生 > 程式設計 >工具庫-基於LibreOffice實現檔案操作

工具庫-基於LibreOffice實現檔案操作

基於libreoffice實現的檔案轉換專案,無框架依賴,即插即用

專案原始碼:github/workable-converter

1. 技術棧

  • LibreOffice:v6.2.3
  • jodconverter:4.2.2
  • PDFBox:2.0.12
  • cglib動態代理 + 懶漢工廠模式 + 策略模式 + 裝飾器模式
  • qtools-property管理配置檔案(application.yml、bootstrap.yml、workable-converter.yml三種命名的配置檔案任意包含一種即可)

2. 功能

  • 支援doc、docx、html、ppt、png、pdf等等型別的檔案互相轉換
  • 支援按照檔案路徑、位元組輸入輸出流、Base64等不同姿勢轉換
  • 不依賴第三方框架,即插即用,支援application.yml、bootstrap.yml、workable-converter.yml三種配置(自己專案中具體配置一個即可)

3. 使用

3.1 安裝配置LibreOffice6.2.3

CentOS請直接參考這篇文章:CentOS7安裝LibreOffice6.2.3

windows跟Mac同樣可以在上述文章中拿到下載連結

安裝完成後,請記住您的LibreOffice的Home目錄,後面需要用到

預設目錄:

  • CentOS: /opt/libreoffice6.2/
  • Mac: /Applications/LibreOffice.app/Contents/
  • Windows: C:\Program Files\LibreOffice\

3.2 獲取依賴

  • Maven
<dependency>
  <groupId>com.liumapp.workable.converter</groupId>
  <artifactId>workable-converter</artifactId>
  <version>v1.2.0</version>
</dependency>複製程式碼
  • Gradle
compile group: 'com.liumapp.workable.converter',name: 'workable-converter',version: 'v1.2.0'複製程式碼

3.3 編輯配置檔案

在專案的resources目錄下,建立一個yml配置檔案,需要確保檔名稱為application.yml、bootstrap.yml或workable-converter.yml三種命名任意一個即可

新增以下配置:

com:
  liumapp:
    workable-converter:
      libreofficePath: "/Applications/LibreOffice.app/Contents"複製程式碼

libreofficePath的值為LibreOffice:6.2.3的安裝目錄

完整的配置項列表如下

引數名 解釋 預設值
libreofficePath LibreOffice安裝目錄 (String) 無預設值,該項必填
libreofficePort LibreOffice監聽埠 (int) 2002
tmpPath 臨時儲存目錄 (String) "./data/"

3.4 執行轉換

3.4.1 按照檔案路徑轉換

以doc轉PDF為例

WorkableConverter converter = new WorkableConverter();//例項化的同時,初始化配置項,配置項的校驗通過Decorator裝飾

ConvertPattern pattern = ConvertPatternManager.getInstance();
pattern.fileToFile("./data/test.doc","./data/pdf/result1.pdf"); //test.doc為待轉換檔案路徑,result1.pdf為轉換結果儲存路徑
pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.DOC);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PDF);

converter.setConverterType(CommonConverterManager.getInstance());//策略模式,後續實現了新的轉換策略後,在此處更換,圖片轉換將考慮使用新的策略來完成
boolean result = converter.convert(pattern.getParameter();複製程式碼

如果要用html轉PDF,將上述程式碼的

pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.DOC);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PDF);複製程式碼

改為

pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.HTML);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PDF);複製程式碼

其他型別的同理

3.4.2 按照輸入輸出流轉換

以doc轉pdf為例

// you can also choice not use proxy
WorkableConverter converter = new WorkableConverter();
ConvertPattern pattern = ConvertPatternManager.getInstance();
pattern.streamToStream(new FileInputStream("./data/test.doc"),new FileOutputStream("./data/pdf/result1_2.pdf"));
// attention !!! convert by stream must set prefix.
pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.DOC);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PDF);
converter.setConverterType(CommonConverterManager.getInstance());
boolean result = converter.convert(pattern.getParameter();複製程式碼

跟上例基本相同,唯一的變化是通過pattern.streamToStream()來設定輸入輸出流,轉換原始檔資料從輸入流中讀取,轉換結果會直接寫入輸出流中,

同時要切換轉換格式,跟上例一樣設定不同的prefix即可

3.4.3 按照檔案Base64轉換

仍以doc轉pdf為例

WorkableConverter converter = new WorkableConverter();
ConvertPattern pattern = ConvertPatternManager.getInstance();
pattern.base64ToBase64(Base64FileTool.FileToBase64(new File("./data/test.doc")));
// attention !!! convert by base64 must set prefix.
pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.DOC);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PDF);
converter.setConverterType(CommonConverterManager.getInstance());
boolean result = converter.convert(pattern.getParameter();
String destBase64 = pattern.getBase64Result();複製程式碼

輸入base64執行轉換,首先通過pattern.base64ToBase64()來設定轉換源的base64值

轉換結果result仍然是一個boolean型別,通過pattern.getBase64Result來獲取轉換結果的base64值

要切換轉換格式,跟上例一樣設定不同的prefix即可

3.5 圖片處理

目前對於圖片的處理,只支援將PDF轉PNG圖片(如果1份pdf檔案有20頁,那麼將會轉換為20張png圖片),該功能的實現基於PDFBox:2.0.12

3.5.1 按照檔案路徑處理

pattern.fileToFiles()第一個引數為待轉換的pdf檔案路徑,第二個引數為轉換後的圖片儲存路徑

WorkableConverter converter = new WorkableConverter();
ConvertPattern pattern = ConvertPatternManager.getInstance();
pattern.fileToFiles("./data/test5.pdf","./data/");
pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.PDF);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PNG);
converter.setConverterType(PdfBoxConverterManager.getInstance()); // pdf box converter manager only support pdf to png
assertEquals(true,converter.convert(pattern.getParameter()));
assertEquals(true,FileTool.isFileExists("./data/test5_0.png"));
assertEquals(true,FileTool.isFileExists("./data/test5_1.png"));
assertEquals(true,FileTool.isFileExists("./data/test5_2.png"));
assertEquals(true,FileTool.isFileExists("./data/test5_3.png"));複製程式碼

3.5.2 按照檔案Base64處理

pattern.base64ToBase64()的引數為待轉換pdf檔案的base64值

轉換結束後,通過List resultBase64 = pattern.getBase64Results()獲取轉換後的圖片base64值的集合

WorkableConverter converter = new WorkableConverter();
ConvertPattern pattern = ConvertPatternManager.getInstance();
pattern.base64ToBase64(Base64FileTool.FileToBase64(new File("./data/test5.pdf")));
pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.PDF);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PNG);
converter.setConverterType(PdfBoxConverterManager.getInstance()); // pdf box converter manager only support pdf to png
boolean result = converter.convert(pattern.getParameter());
List<String> resultBase64 = pattern.getBase64Results();
assertEquals(true,result);
assertEquals(4,resultBase64.size());複製程式碼

3.6 新增水印

水印的轉換策略為WaterMarkConverter

新增水印注意事項

  • 請確保輸入原始檔字尾為PDF,輸出原始檔字尾也為PDF
  • 水印引數需要new一個WaterMarkRequire來設定
  • setWaterMarkPage(int page)代表在哪一頁上新增水印,如果為0,則表示所有頁面
  • 水印本身為一個PDF檔案,該檔案只需要一頁,其第一頁的內容將被視為水印新增到原始檔中
    比如說,要新增透明度為0.3的文字作為水印的話,自己使用word等工具繪製透明度為0.3的字型(或者上包含透明度的png圖片也可以)並另存為一個watermark.pdf檔案複製程式碼
    然後使用waterMarkRequire.setWaterMarkPDFBase64(Base64FileTool.FileToBase64(new File("./data/watermark.pdf")))複製程式碼
    或者waterMarkRequire.setWaterMarkPDFBytes(FileUtils.readFileToByteArray(new File("./data/watermark.pdf")))將該檔案的base64或者bytes值輸入即可複製程式碼

具體使用可以分為三種方式

3.6.1 按照檔案路徑新增水印

WorkableConverter converter = new WorkableConverter();
converter.setConverterType(WaterMarkConverterManager.getInstance());//選擇具體的水印轉換策略

ConvertPattern pattern = ConvertPatternManager.getInstance();
WaterMarkRequire waterMarkRequire = new WaterMarkRequire();//建立水印所需要的引數

//指定在具體的哪一頁新增水印,0的話則在所有頁面新增水印
waterMarkRequire.setWaterMarkPage(0);//0 means all age
waterMarkRequire.setWaterMarkPDFBase64(Base64FileTool.FileToBase64(new File("./data/watermark.pdf")));

pattern.setWaterMarkRequire(waterMarkRequire);
pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.PDF);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PDF);
pattern.fileToFile("./data/test5.pdf","./data/test5_with_mark01.pdf");//新增水印後的檔案儲存在./data/目錄下,名為test5_with_mark01.pdf

boolean result = converter.convert(pattern.getParameter());
assertEquals(true,result);複製程式碼

3.6.2 按照流新增水印

WorkableConverter converter = new WorkableConverter();
converter.setConverterType(WaterMarkConverterManager.getInstance());

ConvertPattern pattern = ConvertPatternManager.getInstance();
WaterMarkRequire waterMarkRequire = new WaterMarkRequire();

waterMarkRequire.setWaterMarkPage(0);//0 means all age
waterMarkRequire.setWaterMarkPDFBytes(FileUtils.readFileToByteArray(new File("./data/watermark.pdf")));

pattern.setWaterMarkRequire(waterMarkRequire);
pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.PDF);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PDF);
pattern.streamToStream(new FileInputStream("./data/test5.pdf"),new FileOutputStream("./data/test5_with_mark02.pdf"));

boolean result = converter.convert(pattern.getParameter());
assertEquals(true,result);複製程式碼

3.6.3 按照base64新增水印

WorkableConverter converter = new WorkableConverter();
converter.setConverterType(WaterMarkConverterManager.getInstance());

ConvertPattern pattern = ConvertPatternManager.getInstance();
WaterMarkRequire waterMarkRequire = new WaterMarkRequire();

waterMarkRequire.setWaterMarkPage(0);//0 means all age
waterMarkRequire.setWaterMarkPDFBase64(Base64FileTool.FileToBase64(new File("./data/watermark.pdf")));

pattern.setWaterMarkRequire(waterMarkRequire);
pattern.setSrcFilePrefix(DefaultDocumentFormatRegistry.PDF);
pattern.setDestFilePrefix(DefaultDocumentFormatRegistry.PDF);
pattern.base64ToBase64(Base64FileTool.FileToBase64(new File("./data/test5.pdf")));

boolean result = converter.convert(pattern.getParameter());
String base64Result = pattern.getBase64Result();
Base64FileTool.saveBase64File(base64Result,"./data/test5_with_mark03.pdf");
assertEquals(true,result);複製程式碼

4. 待辦事項

  • 已經測試通過的有doc、docx、html 按照不同姿勢轉PDF,其他型別的並沒有編寫測試單元,後續考慮增加
  • 目前只支援yml配置,後續考慮新增其他型別的配置支援(xml、properties等)
  • 目前Markdown格式很流行,考慮實現markdown格式的字串轉PDF(markdown -> html -> pdf)

5. 注意事項

  • 因為需要LibreOffice的支援,所以不建議在Docker等容器內執行(LibreOffice暫無Docker穩定發行版的映象)
  • 轉換亂碼、轉換耗時過長,請檢查伺服器是否安裝有中文字型
  • 專案啟動後,在執行第一次轉換任務時,因為涉及到與LibreOffice建立連線等操作,所以會耗時較長,第二次任務及以後穩定在0.5秒以內(具體時間因機器配置會有所差異)

6. 參考連結

  • https://www.libreoffice.org/download/download/?type=rpm-x86_64&version=6.2.3&lang=zh-CN
  • https://api.libreoffice.org/
  • https://github.com/sbraconnier/jodconverter/
  • https://memorynotfound.com/apache-pdfbox-add-watermark-pdf-document/