Java 實現word模板轉為pdf

1. pom相關依賴

工具poi-tl (操作word文檔模板) + jacob (將操作後的word模板轉為pdf)

<!-- poi-tl的pom依賴 --> 
<dependency>
  <groupId>com.deepoove</groupId>
  <artifactId>poi-tl</artifactId>
  <version>1.9.1</version>
</dependency>
<!-- jacob的pom依賴(需自行導入.jar包) -->
<dependency>
  <groupId>com.jacob</groupId>
  <artifactId>jacob</artifactId>
  <version>1.17</version>
  <scope>system</scope>
  <systemPath>${project.basedir}/src/main/resources/lib/jacob.jar</systemPath>
</dependency>

2. 對word模板進行插入數據操作

使用poi-tl操作word需要創建一個用於向word插入數據的Map<String, Object>集合, word模板中標簽格式為”{{標簽}}”, 其中標簽內容為Map<String, Object> 的key.

// 項目根路徑
String abPath = new File("").getAbsolutePath() + "/src/main/resources";
// 創建用於插入數據的Map
Map<String, Object> map = new HashMap<>();
map.put(<k>, <v>);
...
// 填充word文檔
XWPFTemplate template = XWPFTemplate.compile(abPath + "<模板路徑>").render(map);
// 輸出文檔
template.writeAndClose(new FileOutPutStream("<輸出路徑>"));

3. 對word模板的表格執行插入數據操作(動態表格)

使用poi-tl操作word的表格,動態的插入數據,需要用到poi-tl的可選插件進行自定義渲染策略, 首先在word需要操作的表格中的任意單元格添加標簽“{{標簽}}”

自定義渲染策略

/**
 * 自定義渲染策略
 *
 * @author 
 */
public class DetailTablePolicy extends DynamicTableRenderPolicy {
  // 表格起始行行數
  int tableStartRow = 1;

  /**
   * 自定義渲染策略
   *
   * @data 傳入的封裝好的數據
   */
  @Override
  public void render(XWPFTable table, Object data) throws Exception {
    // 如果數據為空,直接返回
    if (null == data) return;
    // 封裝數據List的數據封裝對象
    NdrwhkhzbData detailData = (NdrwhkhzbData) data;
		// 獲取當前列表行高
    int height = table.getRow(2).getHeight();
    // 從封裝對象中獲取數據集合
    List<RowRenderData> datas = detailData.getNdrwhkhzbs();
    if (null != datas) {
      // 循環移除空白表格中數據數量的空白行
      for (int i = 1; i < datas.size() + 2; i++) {
        table.removeRow(i);
      }
      // 循環插入數據
      for (int i = 0; i < datas.size(); i++) {
        // 新增一行空白行
        XWPFTableRow insertNewTableRow = table.insertNewTableRow(tableStartRow);
        // 設置行高
        insertNewTableRow.setHeight(height);
        // 循環添加單元格(4為每行單元格數量)
        for (int j = 0; j < 4; j++) {
          insertNewTableRow.createCell();
        }
        // 填充表格
        TableRenderPolicy.Helper.renderRow(table.getRow(tableStartRow), datas.get(i));
      }
    }
  }
}

把自定義渲染策略當做工具類, 在主邏輯中直接配置使用

	/**
   * 操作年度任務和考核指標表
   *
   * @throws IOException 輸入輸出流異常
   */
  private void createNdrwhkhzb(Integer uid, String dirPath) throws IOException {
    PageData datas = new PageData();
    NdrwhkhzbData detailTable = new NdrwhkhzbData();
    List<RowRenderData> nds = new ArrayList<>();
    // 根據uid查詢年度任務和考核指標數據
    List<NdrwhkhzbEntity> list = ndrwhkhzbService.selectNdrwhkhzbByUid(uid);
    for (NdrwhkhzbEntity ndrwhkhzbEntity : list) {
      RowRenderData rrd = Rows.of(ndrwhkhzbEntity.getNd(), ndrwhkhzbEntity.getNdrw(), ndrwhkhzbEntity.getNdkhzb()
          , ndrwhkhzbEntity.getZyrwdsjjd()).center().create();
      nds.add(rrd);
    }
    detailTable.setNdrwhkhzbs(nds);
    datas.setNdrwhkhzbData(detailTable);

    // 配置表格
    Configure config = Configure.builder().bind("detail_table", new DetailTablePolicy()).build();
    // 調用渲染策略進行填充
    XWPFTemplate template =
        XWPFTemplate.compile(dirPath + "/" + uid + "_Complete.docx", config).render(datas);
    // 寫入表格中
    template.writeToFile(dirPath + "/" + uid + "_Complete.docx");
  }

用到的一些實體類

// PageData
public class PageData {
  @Name("detail_table")
  private NdrwhkhzbData ndrwhkhzbData;

  public NdrwhkhzbData getNdrwhkhzbData() {
    return ndrwhkhzbData;
  }

  public void setNdrwhkhzbData(NdrwhkhzbData ndrwhkhzbData) {
    this.ndrwhkhzbData = ndrwhkhzbData;
  }
}

// NdrwhkhzbData
public class NdrwhkhzbData {
  private List<RowRenderData> ndrwhkhzbs;

  public List<RowRenderData> getNdrwhkhzbs() {
    return ndrwhkhzbs;
  }

  public void setNdrwhkhzbs(List<RowRenderData> ndrwhkhzbs) {
    this.ndrwhkhzbs = ndrwhkhzbs;
  }
}

4. 將編輯好的Word轉為pdf格式(jacob)

這裡將word轉為pdf時需要用到jacob, 這裡需要將jacob的dll文件放到jdk和jre的bin目錄下, 下載的jacob中dll文件一般為兩個版本, X86為32位, X64為64位, 根據自己安裝的jdk版本添加所對應的dll文件

  /*
	* 將 .docx 轉換為 .pdf
	*/
  ActiveXComponent app = null;
  String wordFile = dirPath + "/" + uid + "_Complete.docx";
  String pdfFile = dirPath + "/" + dirName + ".pdf";

  System.out.println("開始轉換...");
  // 開始時間
  long start = System.currentTimeMillis();
  try {
    // 打開word
    app = new ActiveXComponent("Word.Application");
    // 設置word不可見,很多博客下面這裡都寫瞭這一句話,其實是沒有必要的,因為默認就是不可見的,如果設置可見就是會打開一個word文檔,對於轉化為pdf明顯是沒有必要的
    //app.setProperty("Visible", false);
    // 獲得word中所有打開的文檔
    Dispatch documents = app.getProperty("Documents").toDispatch();
    System.out.println("打開文件: " + wordFile);
    // 打開文檔
    Dispatch documentP = Dispatch.call(documents, "Open", wordFile, false, true).toDispatch();
    // 如果文件存在的話,不會覆蓋,會直接報錯,所以我們需要判斷文件是否存在
    File target = new File(pdfFile);
    if (target.exists()) {
      target.delete();
    }
    System.out.println("另存為: " + pdfFile);
    // 另存為,將文檔報錯為pdf,其中word保存為pdf的格式宏的值是17
    Dispatch.call(documentP, "SaveAs", pdfFile, 17);
    // 關閉文檔
    Dispatch.call(documentP, "Close", false);
    // 結束時間
    long end = System.currentTimeMillis();
    System.out.println("轉換成功,用時:" + (end - start) + "ms");
  } catch (Exception e) {
    e.getMessage();
    System.out.println("轉換失敗" + e.getMessage());
  } finally {
    // 關閉office
    app.invoke("Quit", 0);
  }

5. 通過lo流將生成好的文件傳到瀏覽器下載

/*
 * 下載pdf
 */
String fileName = dirName + ".pdf";
File file = new File(dirPath + "/" + fileName);
if (file.exists()) {
  BufferedInputStream bis = null;
  FileInputStream fis = null;
  try {
    response.setHeader("Content-disposition", "attachment; filename=" + fileName);
    byte[] buff = new byte[2048];
    fis = new FileInputStream(file);
    bis = new BufferedInputStream(fis);
    OutputStream os = response.getOutputStream();
    int i = bis.read(buff);
    while (i != -1) {
      os.write(buff, 0, i);
      i = bis.read(buff);
    }
    os.close();
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    assert fis != null;
    fis.close();
    assert bis != null;
    bis.close();
  }
}

6. 最後的Controller整體代碼

package org.example.controller;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.Includes;
import com.deepoove.poi.data.RowRenderData;
import com.deepoove.poi.data.Rows;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import org.example.entity.*;
import org.example.service.*;
import org.example.utils.DetailTablePolicy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 創建pdf控制器
 *
 * @author: yoojyn
 * @data: 2021/1/11
 */
@Controller
@RequestMapping("/createPdfController")
public class CreatePdfController {
  @Autowired
  private IKtfmService ktfmService;
  @Autowired
  private IKtjbxxService ktjbxxService;
  @Autowired
  private IKtbyxfxService ktbyxfxService;
  @Autowired
  private IZtmbhkhzbService ztmbhkhzbService;
  @Autowired
  private INdrwhkhzbService ndrwhkhzbService;
  @Autowired
  private IKtjfysjsmService ktjfysjsmService;
  @Autowired
  private IXjxhkxxfxService xjxhkxxfxService;

  /**
   * 生成word文件
   *
   * @param session 作用域
   */
  @Scope("prototype")
  @ResponseBody
  @RequestMapping("/createPdf")
  public void createPdf(HttpSession session, HttpServletResponse response) {
    // 獲取當前用戶id
    Userinfo loginedUser = (Userinfo) session.getAttribute("loginedUser");
    Integer uid = loginedUser.getUid();
    String dirName = DigestUtils.md5DigestAsHex((uid + "_國傢重大專項任務合同申報").getBytes());
    String dirPath = "D:/" + dirName;
    String abPath = new File("").getAbsolutePath() + "/src/main/resources";

    try {
      // 創建用於存儲中間文件的文件夾
      new File(dirPath).mkdirs();
      // 創建用於存儲數據的map集合
      Map<String, Object> map = new HashMap<>();
      // 獲取封面數據
      createKtfm(uid, map);
      // 獲取基本信息數據
      createJbxx(uid, map);
      // 獲取必要性分析
      createByxfx(uid, map);
      // 獲取總體目標和考核指標
      createZtmbhkhzb(uid, map);
      // 獲取經費預算及說明
      createJfysjsm(uid, map);
      // 查詢附件
      XjxhkxxfxEntity xjxhkxxfxEntity = xjxhkxxfxService.selectXjxhkxxfxByUid(uid);
      // 設置下一步處理表格要用到的標簽
      map.put("page9",
          Includes.ofLocal(abPath + "/static/file/upload/" + xjxhkxxfxEntity.getFilename()).create());
      map.put("detail_table", "{{detail_table}}");
      // 填充文檔
      XWPFTemplate template = XWPFTemplate.compile(abPath + "/static/file/moban/moban.docx").render(map);
      // 輸出文檔
      template.writeAndClose(new FileOutputStream(dirPath + "/" + uid + "_Complete.docx"));
      // 操作年度任務和考核指標表
      createNdrwhkhzb(uid, dirPath);
    } catch (IOException e) {
      e.printStackTrace();
    }

    try {
      /*
       * 將 .docx 轉換為 .pdf
       */
      ActiveXComponent app = null;
      String wordFile = dirPath + "/" + uid + "_Complete.docx";
      String pdfFile = dirPath + "/" + dirName + ".pdf";

      System.out.println("開始轉換...");
      // 開始時間
      long start = System.currentTimeMillis();
      try {
        // 打開word
        app = new ActiveXComponent("Word.Application");
        // 設置word不可見,很多博客下面這裡都寫瞭這一句話,其實是沒有必要的,因為默認就是不可見的,如果設置可見就是會打開一個word文檔,對於轉化為pdf明顯是沒有必要的
        //app.setProperty("Visible", false);
        // 獲得word中所有打開的文檔
        Dispatch documents = app.getProperty("Documents").toDispatch();
        System.out.println("打開文件: " + wordFile);
        // 打開文檔
        Dispatch documentP = Dispatch.call(documents, "Open", wordFile, false, true).toDispatch();
        // 如果文件存在的話,不會覆蓋,會直接報錯,所以我們需要判斷文件是否存在
        File target = new File(pdfFile);
        if (target.exists()) {
          target.delete();
        }
        System.out.println("另存為: " + pdfFile);
        // 另存為,將文檔報錯為pdf,其中word保存為pdf的格式宏的值是17
        Dispatch.call(documentP, "SaveAs", pdfFile, 17);
        // 關閉文檔
        Dispatch.call(documentP, "Close", false);
        // 結束時間
        long end = System.currentTimeMillis();
        System.out.println("轉換成功,用時:" + (end - start) + "ms");
      } catch (Exception e) {
        e.getMessage();
        System.out.println("轉換失敗" + e.getMessage());
      } finally {
        // 關閉office
        app.invoke("Quit", 0);
      }

      /*
       * 下載pdf
       */
      String fileName = dirName + ".pdf";
      File file = new File(dirPath + "/" + fileName);
      if (file.exists()) {
        BufferedInputStream bis = null;
        FileInputStream fis = null;
        try {
          response.setHeader("Content-disposition", "attachment; filename=" + fileName);
          byte[] buff = new byte[2048];
          fis = new FileInputStream(file);
          bis = new BufferedInputStream(fis);
          OutputStream os = response.getOutputStream();
          int i = bis.read(buff);
          while (i != -1) {
            os.write(buff, 0, i);
            i = bis.read(buff);
          }
          os.close();
        } catch (Exception e) {
          e.printStackTrace();
        } finally {
          assert fis != null;
          fis.close();
          assert bis != null;
          bis.close();
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      delDir(new File(dirPath));
    }
  }

  /**
   * 刪除文件夾
   *
   * @param file 文件夾對象
   */
  private void delDir(File file) {
    if (file.isFile()) {
      file.delete();
    }
    if (file.isDirectory()) {
      File[] files = file.listFiles();
      for (File f : files) {
        f.delete();
      }
      file.delete();
    }
  }

  /**
   * 儲存經費預算及說明
   *
   * @param uid 用戶id
   * @param map 儲存數據的map集合
   */
  private void createJfysjsm(Integer uid, Map<String, Object> map) {
    // 根據用戶編號查詢經費預算及說明
    KtjfysjsmEntity ktjfysjsmEntity = ktjfysjsmService.getDatesByUid(uid);
    // 添加到map集合
    map.put("zjzyczzj", ktjfysjsmEntity.getZjzyczzj());
    map.put("zjdfczzj", ktjfysjsmEntity.getZjdfczzj());
    map.put("zjdwzczj", ktjfysjsmEntity.getZjdwzczj());
    map.put("zjqt", ktjfysjsmEntity.getZjqt());
  }

  /**
   * 操作年度任務和考核指標表
   *
   * @throws IOException 輸入輸出流異常
   */
  private void createNdrwhkhzb(Integer uid, String dirPath) throws IOException {
    PageData datas = new PageData();
    NdrwhkhzbData detailTable = new NdrwhkhzbData();
    List<RowRenderData> nds = new ArrayList<>();
    // 根據uid查詢年度任務和考核指標數據
    List<NdrwhkhzbEntity> list = ndrwhkhzbService.selectNdrwhkhzbByUid(uid);
    for (NdrwhkhzbEntity ndrwhkhzbEntity : list) {
      RowRenderData rrd = Rows.of(ndrwhkhzbEntity.getNd(), ndrwhkhzbEntity.getNdrw(), ndrwhkhzbEntity.getNdkhzb()
          , ndrwhkhzbEntity.getZyrwdsjjd()).center().create();
      nds.add(rrd);
    }
    detailTable.setNdrwhkhzbs(nds);
    datas.setNdrwhkhzbData(detailTable);

    Configure config = Configure.builder().bind("detail_table", new DetailTablePolicy()).build();
    XWPFTemplate template =
        XWPFTemplate.compile(dirPath + "/" + uid + "_Complete.docx", config).render(datas);
    template.writeToFile(dirPath + "/" + uid + "_Complete.docx");
  }

  /**
   * 儲存總體目標和考核指標
   *
   * @param uid 用戶id
   * @param map 儲存數據的map集合
   */
  private void createZtmbhkhzb(Integer uid, Map<String, Object> map) {
    // 根據用戶編號查詢總體目標和考核指標
    ZtmbhkhzbEntity ztmbhkhzbEntity = ztmbhkhzbService.selectZtmbhkhzbByUid(uid);
    // 添加到map集合
    map.put("page6", ztmbhkhzbEntity.getZtmbhkhzb());
  }

  /**
   * 儲存必要性分析數據
   *
   * @param uid 用戶id
   * @param map 儲存數據的map集合
   */
  private void createByxfx(Integer uid, Map<String, Object> map) {
    // 根據用戶編號查詢必要性分析數據
    KtbyxfxEntityWithBLOBs ktbyxfxEntity = ktbyxfxService.selectKtbyxfxByUid(uid);
    // 添加到map集合
    map.put("page5_ktyzx", ktbyxfxEntity.getKtyzx());
    map.put("page5_ktysfgc", ktbyxfxEntity.getKtysf());
    map.put("page5_ktyq", ktbyxfxEntity.getKtyq());
  }

  /**
   * 儲存基本信息數據
   *
   * @param uid 用戶編號
   * @param map 儲存數據的map集合
   */
  private void createJbxx(Integer uid, Map<String, Object> map) {
    // 根據用戶編號查詢基本信息數據
    KcjbxxEntity kcjbxxEntity = ktjbxxService.selectKtjbxxByUid(uid);
    // 添加到map集合
    map.put("page3_ktmc", kcjbxxEntity.getKtmc());
    map.put("page3_ktmj", kcjbxxEntity.getKtmj());
    map.put("page3_yjwcsj", kcjbxxEntity.getYjwcsj());
    map.put("page3_kyhdlx", kcjbxxEntity.getKthdlx());
    map.put("page3_yqcglx", kcjbxxEntity.getYqcglx());
    map.put("page3_dwmc", kcjbxxEntity.getDwmc());
    map.put("page3_dwxz", kcjbxxEntity.getDwxz());
    map.put("page3_txdz", kcjbxxEntity.getTxdz());
    map.put("page3_yzbm", kcjbxxEntity.getYzbm());
    map.put("page3_szdq", kcjbxxEntity.getSzdq());
    map.put("page3_dwzgbm", kcjbxxEntity.getDwzgbm());
    map.put("page3_lxdh", kcjbxxEntity.getLxdh());
    map.put("page3_zzjgdm", kcjbxxEntity.getZzjgdm());
    map.put("page3_czhm", kcjbxxEntity.getCzhm());
    map.put("page3_dwclsj", kcjbxxEntity.getDwclsj());
    map.put("page3_dzxx", kcjbxxEntity.getDzxx());
  }

  /**
   * 儲存課題封面數據
   *
   * @param uid 用戶編號
   * @param map 儲存數據的map集合
   */
  private void createKtfm(Integer uid, Map<String, Object> map) {
    // 根據用戶編號查詢封面數據
    KtfmEntity ktfmEntity = ktfmService.selectKtfmByUid(uid);
    // 添加到map集合
    map.put("page1_zxmc", "5G總體及關鍵器件");
    map.put("page1_xmbh", "2016ZX03001_001");
    map.put("page1_xmmc", "新一代寬帶無線移動通信網");
    map.put("page1_ktbh", "2016ZX03001_001_002");
    map.put("page1_ktmc", "5G高性能基站A/D、D/A轉換器試驗樣片研發");
    map.put("page1_zrdw", "program_test");
    map.put("page1_ktzz", ktfmEntity.getKtfzr());
    map.put("page1_ktnx1", "2016-01-01");
    map.put("page1_ktnx2", "2017-12-31");
    map.put("page1_tbrq", "2020-12-28");
    map.put("page1_nian", "二一");
    map.put("page1_yue", "一");
  }

}

以上就是Java 實現word模板轉為pdf的詳細內容,更多關於Java word模板轉為pdf的資料請關註WalkonNet其它相關文章!

推薦閱讀: