Java操作Word

lamar醬發表於2019-07-20

Poi工具

Apache的POI,是Apache軟體基金會的開放原始碼函式庫,POI提供API給Java程式對Microsoft Office格式檔案讀和寫的功能。POI讀寫Excel功能強大、操作簡單。但是POI操作時,一般只用它讀取word文件,POI只能能夠建立簡單的word文件,相對而言POI操作時的功能太少。

新增Poi依賴

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.15</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>3.15</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.15</version>
</dependency>
複製程式碼

匯出

1)匯出語句如下

public void upload() throws IOException {
XWPFDocument doc = new XWPFDocument();// 建立Word檔案
    XWPFParagraph p = doc.createParagraph();// 新建一個段落
    p.setAlignment(ParagraphAlignment.CENTER);// 設定段落的對齊方式
    p.setBorderBottom(Borders.DOUBLE);//設定下邊框
    p.setBorderTop(Borders.DOUBLE);//設定上邊框
    p.setBorderRight(Borders.DOUBLE);//設定右邊框
    p.setBorderLeft(Borders.DOUBLE);//設定左邊框
    XWPFRun r = p.createRun();//建立段落文字
    r.setText("POI建立的Word段落文字");
    r.setBold(true);//設定為粗體
    r.setColor("FF0000");//設定顏色
    p = doc.createParagraph();// 新建一個段落
    r = p.createRun();
    r.setText("POI讀寫Excel功能強大、操作簡單。");
    XWPFTable table= doc.createTable(3, 3);//建立一個表格
    table.getRow(0).getCell(0).setText("表格1");
    table.getRow(1).getCell(1).setText("表格2");
    table.getRow(2).getCell(2).setText("表格3");
    FileOutputStream out = new FileOutputStream("D:\\記事本\\公司\\word\\sample.doc");
    doc.write(out);
    out.close();
}
複製程式碼

2)呼叫介面。匯出後的word如下。

Java操作Word

匯入

1)匯入語句如下。

public void importWord(@RequestParam("file") MultipartFile file) throws IOException {
if (file == null) {
        System.out.println("檔案不能為空");
    }
    // 建立Word檔案
    XWPFDocument doc = new XWPFDocument(file.getInputStream());
    //遍歷段落
    for (XWPFParagraph p : doc.getParagraphs())
    {
        System.out.println(p.getParagraphText());
    }
    //遍歷表格
    for (XWPFTable table : doc.getTables())
    {
        for (XWPFTableRow row : table.getRows()) {
            for (XWPFTableCell cell : row.getTableCells()) {
                System.out.println(cell.getText());
            }
        }
    }
}
複製程式碼

2)呼叫介面,匯入之前匯出的word文字。匯入後的控制檯輸出如下。

Java操作Word

使用Freemarker模板進行生成

優點:採用FreeMarker是匯出Word的最佳實現,非常的靈活,能夠按照自己指定的樣式設定並輸出內容,操作簡單方便,程式碼實現也容易。程式碼量少,樣式、內容容易控制,列印不變形,完全符合office標準。

**缺點:**需要提前設計好word模板,把需要替換的地方用特殊標記標出來。

新增freemarker依賴

<!--引入freemarker 模板依賴-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
複製程式碼

匯出

1)準備要匯出的word模板

Java操作Word

2)製作基本樣式之後,另存為.xml格式文件

Java操作Word

3)開啟.xml檔案,在相應的地方填入${content}表示式

Java操作Word
4)使用文字編輯器sublime開啟

Java操作Word

5)複製黏貼至專案檔案中,字尾名更改為.ftl

Java操作Word

6)編寫匯出類

package com.lamarsan.word_demo.freemarker;

import freemarker.template.*;
import sun.misc.BASE64Encoder;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

/**
 * className: ExportMyWord
 * description: TODO
 *
 * @author hasee
 * @version 1.0
 * @date 2019/7/15 14:54
 */
public class ExportMyWord {
    private Logger log = Logger.getLogger(ExportMyWord.class.toString());
    private Configuration config = null;

    public ExportMyWord() {
        config = new Configuration();
        config.setDefaultEncoding("utf-8");
    }

    /**
     * FreeMarker生成Word
     * @param dataMap 資料
     * @param templateName 目標名
     * @param saveFilePath 儲存檔案路徑的全路徑名(路徑+檔名)
     * @Author Huang Xiaocong 2018年12月15日 下午10:19:03
     */
    public void createWord(Map<String, Object> dataMap, String templateName, String saveFilePath) {
        //載入模板(路徑)資料
        config.setClassForTemplateLoading(this.getClass(), "/templates");
        //設定異常處理器 這樣的話 即使沒有屬性也不會出錯 如:${list.name}...不會報錯
        config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        Template template = null;
        if(templateName.endsWith(".ftl")) {
            templateName = templateName.substring(0, templateName.indexOf(".ftl"));
        }
        try {
            template = config.getTemplate(templateName + ".ftl");
        } catch (TemplateNotFoundException e) {
            log.info("模板檔案未找到");
            e.printStackTrace();
        } catch (MalformedTemplateNameException e) {
            log.info("模板型別不正確");
            e.printStackTrace();
        } catch (IOException e) {
            log.info("IO讀取失敗");
            e.printStackTrace();
        }
        File outFile = new File(saveFilePath);
        if(!outFile.getParentFile().exists()) {
            outFile.getParentFile().mkdirs();
        }
        Writer out = null;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(outFile);
        } catch (FileNotFoundException e) {
            log.info("輸出檔案時未找到檔案");
            e.printStackTrace();
        }
        out = new BufferedWriter(new OutputStreamWriter(fos));
        //將模板中的預先的程式碼替換為資料
        try {
            template.process(dataMap, out);
        } catch (TemplateException e) {
            log.info("填充模板時異常");
            e.printStackTrace();
        } catch (IOException e) {
            log.info("IO讀取時異常");
            e.printStackTrace();
        }
        log.info("由模板檔案:" + templateName + ".ftl" + " 生成檔案 :" + saveFilePath + " 成功!!");
        try {
            out.close();//web專案不可關閉
        } catch (IOException e) {
            log.info("關閉Write物件出錯");
            e.printStackTrace();
        }
    }
    /**
     * 獲得圖片的Base64編碼
     * @param imgFile
     * @return
     * @Author Huang Xiaocong 2018年12月15日 下午10:15:10
     */
    public String getImageStr(String imgFile) {
        InputStream in = null;
        byte[] data = null;
        try {
            in = new FileInputStream(imgFile);
        } catch (FileNotFoundException e) {
            log.info("載入圖片未找到");
            e.printStackTrace();
        }
        try {
            data = new byte[in.available()];
            //注:FileInputStream.available()方法可以從輸入流中阻斷由下一個方法呼叫這個輸入流中讀取的剩餘位元組數
            in.read(data);
            in.close();
        } catch (IOException e) {
            log.info("IO操作圖片錯誤");
            e.printStackTrace();
        }
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(data);

    }

}
複製程式碼

需要注意的是,在載入config.setClassForTemplateLoading(this.getClass(), "/templates");時,需要指定存放freemarker檔案的檔案目錄地址,即templates,不然會報org.springframework.beans.factory.BeanCreationException: Error creating bean with...錯誤,即找不到模板檔案。

7)編寫main方法

public static void main(String[] args) {
ExportMyWord emw = new ExportMyWord();
    Map<String, Object> dataMap = new HashMap<String, Object>();
    dataMap.put("name", "傅澤鵬");
    dataMap.put("age", 20);
    dataMap.put("sex", "男");
    dataMap.put("imgheader", emw.getImageStr("D:\\圖片\\130.png"));
    dataMap.put("phoneNum", "13588097790");
    dataMap.put("project", "計算機");
    dataMap.put("address", "杭州");
    dataMap.put("natureWork", "全職");
    dataMap.put("industry", "IT");
    dataMap.put("application", "Java開發");
    dataMap.put("time", "2017年-2021年");
    dataMap.put("schoolname", "浙江科技學院");
    dataMap.put("education", "本科");
    dataMap.put("projectExperience", "辯論小程式開發");
    dataMap.put("introduce", "喜歡二次元,程式設計的大學生");
    emw.createWord(dataMap, "information.ftl", "D:\\記事本\\公司\\簡歷.docx");
}
複製程式碼

8)修改ftl檔案的圖片程式碼

如果立馬點選生成docx檔案,會發現圖片變成了一堆超級長的程式碼。所以可以判斷是ftl處的程式碼有誤。進入ftl程式碼,查詢${imgheader},修改<w:p>標籤如下。

<w:p>
<w:pPr>
        <w:widowControl w:val="off"/>
        <w:jc w:val="center"/>
        <w:rPr>
            <w:rFonts w:hint="default"/>
            <w:b w:val="off"/>
            <w:sz w:val="24"/>
            <w:sz-cs w:val="32"/>
            <w:vertAlign w:val="baseline"/>
            <w:lang w:val="EN-US" w:fareast="ZH-CN"/>
        </w:rPr>
    </w:pPr>
    <w:r>
        <w:rPr>
            <w:rFonts w:hint="fareast"/>
            <w:b w:val="off"/>
            <w:sz w:val="24"/>
            <w:sz-cs w:val="32"/>
            <w:vertAlign w:val="baseline"/>
            <w:lang w:val="EN-US" w:fareast="ZH-CN"/>
        </w:rPr>
        <w:pict>
            <w:binData w:name="wordml://1.png">${imgheader}</w:binData>
            <v:shape id="_x0000_s1026" o:spt="75" alt="" type="#_x0000_t75"
                     style="height:119.8pt;width:74.65pt;" filled="f" o:preferrelative="t"
                     stroked="f" coordsize="21600,21600">
                <v:path/>
                <v:fill on="f" focussize="0,0"/>
                <v:stroke on="f"/>
                <v:imagedata src="wordml://1.png" o:title="140"/>
                <o:lock v:ext="edit" aspectratio="t"/>
                <w10:wrap type="none"/>
                <w10:anchorlock/>
            </v:shape>
        </w:pict>
    </w:r>
</w:p>
複製程式碼

9)最終效果圖

Java操作Word

補充迴圈操作

如果需要多重迴圈可以用list來實現。

1)目標模板如下:

Java操作Word

2)查詢欄位,在<w:tr>前加入<#list list as list>,</w:tr>後加入</#list>。第一個list 不能變,第二個list 為變數名,第三個list 為別名。

3)將time改成list.time,schoolname改成list.schoolname,education改成list.eduxcation。

4)執行程式碼如下:

public static void main(String[] args) {
    ExportMyWord emw = new ExportMyWord();
    Map<String, Object> dataMap = new HashMap<String, Object>();
    dataMap.put("name", "傅澤鵬");
    dataMap.put("age", 20);
    dataMap.put("sex", "男");
    dataMap.put("imgheader", emw.getImageStr("D:\\圖片\\130.png"));
    dataMap.put("phoneNum", "13588097790");
    dataMap.put("project", "計算機");
    dataMap.put("address", "杭州");
    dataMap.put("natureWork", "全職");
    dataMap.put("industry", "IT");
    dataMap.put("application", "Java開發");
    List<Map<String, Object>> newsList = new ArrayList<Map<String, Object>>();
    Map<String, Object> map = new HashMap<String, Object>();
    Map<String, Object> map2 = new HashMap<String, Object>();
    Map<String, Object> map3 = new HashMap<String, Object>();
    map.put("time", "2017年-2021年");
    map.put("schoolname", "浙江科技學院");
    map.put("education", "本科");
    map2.put("time", "2014年-2017年");
    map2.put("schoolname", "瑞安四中");
    map2.put("education", "高中");
    map3.put("time", "2011年-2014年");
    map3.put("schoolname", "莘塍一中");
    map3.put("education", "初中");
    newsList.add(map);
    newsList.add(map2);
    newsList.add(map3);
    //注意list 的名字
    dataMap.put("list", newsList);
    dataMap.put("projectExperience", "辯論小程式開發");
    dataMap.put("introduce", "喜歡二次元,程式設計的大學生");
    emw.createWord(dataMap, "information_list.ftl", "D:\\記事本\\公司\\簡歷2.docx");
}
複製程式碼

6)最終結果圖:

Java操作Word

專案github地址:github.com/lamarsan/wo…

相關文章