poi根據模板導出word文檔


POI結構與常用類

Apache POI是Apache軟件基金會的開源項目,POI提供API給Java程序對Microsoft Office格式檔案讀和寫的功能。 .NET的開發人員則可以利用NPOI (POI for .NET) 來存取 Microsoft Office文檔的功能。

包名稱說明

  • HSSF提供讀寫Microsoft Excel XLS格式檔案的功能。
  • XSSF提供讀寫Microsoft Excel OOXML XLSX格式檔案的功能。
  • HWPF提供讀寫Microsoft Word DOC格式檔案的功能。
  • HSLF提供讀寫Microsoft PowerPoint格式檔案的功能。
  • HDGF提供讀Microsoft Visio格式檔案的功能。
  • HPBF提供讀Microsoft Publisher格式檔案的功能。
  • HSMF提供讀Microsoft Outlook格式檔案的功能。

 

測試例子

測試模板:

 

測試代碼:

@Test
    public void docxExportTest() throws IOException {
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            //獲取docx解析對象
            is = new FileInputStream("F:\\document\\咨詢服務合同.docx");
            XWPFDocument document = new XWPFDocument(is);

            //組裝參數
            File seal = new File("F:\\imgtest\\1.jpg");
            Map<String, Object> sealMap = new HashMap<String, Object>();
            sealMap.put("width", 50);
            sealMap.put("height", 50);
            sealMap.put("type", "jpg");
            sealMap.put("content", new FileInputStream(seal));

            Map<String, Object> contentMap = new HashMap<>();
            contentMap.put("part_a", "張三");
            contentMap.put("address_a", "青島市市南區動漫產業園E座");
            contentMap.put("legal_person_a", "李四");
            contentMap.put("seal", sealMap);

            //解析替換段落文本對象
            XWPFUtil.changeParagraph(document, contentMap);

            //生成新的word文檔
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String fileName = "咨詢服務合同" + sdf.format(new Date()) + ".docx";
            File file = new File("F://" + fileName);
            fos = new FileOutputStream(file);
            document.write(fos);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                is.close();
            }
            if (fos != null) {
                fos.close();
            }
        }
    }

工具類:

package com.m2plat.puhui.utils;

import com.alibaba.fastjson.JSON;
import org.apache.commons.io.IOUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

/**
 * Created by xiangzh on 2018/11/1.
 */
public class XWPFUtils {

    private static Logger logger = LoggerFactory.getLogger(XWPFUtils.class);

    /**
     * 根據模板生成word文檔
     * @param os
     * @param tempPath
     * @param contentMap
     */
    public static void writeTemp(OutputStream os, String tempPath, Map<String, Object> contentMap){
        logger.info("---根據模板生成word文檔");
        logger.info("---模板地址:{}",tempPath);
        logger.info("---替換內容:{}", JSON.toJSONString(contentMap));
        ClassPathResource resource = new ClassPathResource(tempPath);
        if(resource == null || !resource.exists()){
            logger.error("---模板文件不存在,tempPath:{}",tempPath);
            return;
        }

        InputStream is = null;
        try{
            is = resource.getInputStream();
            XWPFDocument document = new XWPFDocument(is);
            XWPFUtils.changeParagraph(document, contentMap);
            //生成新的word文檔
            document.write(os);
        }catch (IOException e){
            logger.error("---輸出word文檔失敗,原因:{}",e.getMessage());
        }finally {
            IOUtils.closeQuietly(is);
        }
    }


    /**
     * 替換段落文本
     *
     * @param document docx解析對象
     * @param textMap  需要替換的信息集合
     */
    public static void changeParagraph(XWPFDocument document, Map<String, Object> textMap) {
        //獲取段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();

        for (XWPFParagraph paragraph : paragraphs) {
            List<XWPFRun> runs = paragraph.getRuns();
            for (XWPFRun run : runs) {
                String text = run.getText(0);
                //判斷文本是否需要進行替換
                if (checkText(text)) {
                    for (Map.Entry<String, Object> entry : textMap.entrySet()) {
                        //匹配模板與替換值 格式${key}
                        String key = "${" + entry.getKey() + "}";
                        Object value = entry.getValue();

                        if (text.contains(key)) {
                            if (value instanceof String) { //文字替換
                                text = text.replace(key, (String) value);
                            } else if (value instanceof Map) { //圖片替換
                                text = text.replace(key, "");
                                Map picMap = (Map) value;
                                int width = Integer.parseInt(picMap.get("width").toString());
                                int height = Integer.parseInt(picMap.get("height").toString());
                                int picType = getPictureType(picMap.get("type").toString());
                                FileInputStream fis = (FileInputStream) picMap.get("content");
                                try {
                                    String blipId = document.addPictureData(fis, picType);
                                    int id = document.getNextPicNameNumber(picType);
                                    XWPFUtils.createPicture(id, blipId, width, height, run);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                    //替換模板原來位置
                    run.setText(text, 0);
                }
            }
        }
    }

    /**
     * @param id
     * @param blipId
     * @param width 寬
     * @param height 高
     //* @param paragraph  段落
     */
    private static void createPicture(int id, String blipId, int width, int height,XWPFRun xwpfRun) {
        final int EMU = 9525;
        width *= EMU;
        height *= EMU;
        CTInline inline = xwpfRun.getCTR().addNewDrawing().addNewInline();
        //CTInline inline = paragraph.createRun().getCTR().addNewDrawing().addNewInline(); //在遍歷run列表的時候,創建新的run有可能會導致報錯
        String picXml = ""
                + "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
                + "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
                + "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
                + "         <pic:nvPicPr>" + "            <pic:cNvPr id=\""
                + id
                + "\" name=\"Generated\"/>"
                + "            <pic:cNvPicPr/>"
                + "         </pic:nvPicPr>"
                + "         <pic:blipFill>"
                + "            <a:blip r:embed=\""
                + blipId
                + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>"
                + "            <a:stretch>"
                + "               <a:fillRect/>"
                + "            </a:stretch>"
                + "         </pic:blipFill>"
                + "         <pic:spPr>"
                + "            <a:xfrm>"
                + "               <a:off x=\"0\" y=\"0\"/>"
                + "               <a:ext cx=\""
                + width
                + "\" cy=\""
                + height
                + "\"/>"
                + "            </a:xfrm>"
                + "            <a:prstGeom prst=\"rect\">"
                + "               <a:avLst/>"
                + "            </a:prstGeom>"
                + "         </pic:spPr>"
                + "      </pic:pic>"
                + "   </a:graphicData>" + "</a:graphic>";

        inline.addNewGraphic().addNewGraphicData();
        XmlToken xmlToken = null;
        try {
            xmlToken = XmlToken.Factory.parse(picXml);
        } catch (XmlException xe) {
            xe.printStackTrace();
        }
        inline.set(xmlToken);

        inline.setDistT(0);
        inline.setDistB(0);
        inline.setDistL(0);
        inline.setDistR(0);

        CTPositiveSize2D extent = inline.addNewExtent();
        extent.setCx(width);
        extent.setCy(height);

        CTNonVisualDrawingProps docPr = inline.addNewDocPr();
        docPr.setId(id);
        docPr.setName("docx_img_ " + id);
        docPr.setDescr("docx Picture");
    }

    /**
     * 判斷文本中是否包含$
     *
     * @param text 文本
     * @return 包含返回true, 不包含返回false
     */
    private static boolean checkText(String text) {
        if (text == null || "".equals(text)) {
            return false;
        }
        return text.contains("$");
    }

    /**
     * 根據圖片類型,取得對應的圖片類型代碼
     *
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType) {
        int res = XWPFDocument.PICTURE_TYPE_PICT;
        if (picType != null) {
            if (picType.equalsIgnoreCase("png")) {
                res = XWPFDocument.PICTURE_TYPE_PNG;
            } else if (picType.equalsIgnoreCase("dib")) {
                res = XWPFDocument.PICTURE_TYPE_DIB;
            } else if (picType.equalsIgnoreCase("emf")) {
                res = XWPFDocument.PICTURE_TYPE_EMF;
            } else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {
                res = XWPFDocument.PICTURE_TYPE_JPEG;
            } else if (picType.equalsIgnoreCase("wmf")) {
                res = XWPFDocument.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }
}
View Code

測試結果:

 

注意事項

1.實際開發過程中,模板通常存放在項目工程中,獲取模板的代碼如下:

String tempPath = "static/exportTemplates/咨詢服務合同.docx";
ClassPathResource resource = new ClassPathResource(tempPath);
InputStream is = resource.getInputStream();
XWPFDocument document = new XWPFDocument(is);

2.導出文件時如果報錯:Failed to read zip entry source,是因為打包編譯將文件解壓縮導致出問題,解決辦法為在pom文件中配置nonFilteredFileExtension:

<plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <delimiters>
                        <delimiter>@</delimiter>
                        <delimiter>${*}</delimiter>
                    </delimiters>
                    <useDefaultDelimiters>false</useDefaultDelimiters>
                    <encoding>UTF-8</encoding><!-- 指定編碼格式,否則在DOS下運行mvn命令時當發生文件資源copy時將使用系統默認使用GBK編碼 -->
                    <nonFilteredFileExtensions>
                        <nonFilteredFileExtension>bar</nonFilteredFileExtension>
                        <nonFilteredFileExtension>zip</nonFilteredFileExtension>
                        <nonFilteredFileExtension>txt</nonFilteredFileExtension>
                        <nonFilteredFileExtension>pdf</nonFilteredFileExtension>
                        <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                        <nonFilteredFileExtension>xlsx</nonFilteredFileExtension>
                        <nonFilteredFileExtension>xls</nonFilteredFileExtension>
                        <nonFilteredFileExtension>docx</nonFilteredFileExtension>
                        <nonFilteredFileExtension>doc</nonFilteredFileExtension>
                    </nonFilteredFileExtensions>
                </configuration>
            </plugin>

 3.所有導出參數必須轉換成string類型后才能導出,否則替換不了。

 

 

 

參考:

POI使用詳解

POI通過XWPFDocument方式操作word2007

POI操作word模板並生成新的word.docx

war在服務器上讀取文件報:java.io.IOException: Failed to read zip entry source

SpringBoot打成jar包后,讀取resources目錄下的文件

在Springboot中從項目中讀取文件並下載時 ResourceUtils.getFile返回路徑出錯並解決


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM