java中使用POI通过word模板填充数据导出

word模板格式

模板中${}为占位符,占位符可能会出现分在多个<w:r>的情况。使用占位符时,通过无格式的文本编辑器将占位符编辑好,在复制到word中。

另一种方式通过修改xml文件修改

将word文件另存为xml文件通过修改xml文件去修改占位符,然后在转化为word。(此方法我还未实现过我通过此方法遇到过bug,转换成word后占位符依旧是分开的,有些原来好的占位符也会变成分开状态。)

xml文件打开如下:

//部分xml文件
<w:p>
<w:pPr>
<w:jc w:val="center"/>
<w:rPr>
<w:rFonts w:hint="eastAsia" w:eastAsiaTheme="minorEastAsia"/>
<w:lang w:val="en-US" w:eastAsia="zh-CN"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>${</w:t>
</w:r>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>contractName</w:t>
</w:r>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>}</w:t>
</w:r>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:lang w:val="en-US" w:eastAsia="zh-CN"/>
</w:rPr>
<w:t xml:space="preserve"> </w:t>
</w:r>
</w:p>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
<w:rPr>
<w:rFonts w:hint="eastAsia" w:eastAsiaTheme="minorEastAsia"/>
<w:lang w:val="en-US" w:eastAsia="zh-CN"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>${changeName}</w:t>
</w:r>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:lang w:val="en-US" w:eastAsia="zh-CN"/>
</w:rPr>
<w:t xml:space="preserve"> </w:t>
</w:r>
</w:p>

占位符分在多个run中,此时数据无法替换。需修改在用一个run中

工具类

@Slf4j

public class DOCUtils {



    /**

     * 导出docx

     * @param templatePath

     * @param

     * @param map

     */

    public static boolean  getDocx(String templatePath, String fileName, Map<String, String> map , HttpServletResponse response){

        XWPFDocument document = null;

        try{

            File file = new File(templatePath);

            InputStream in = new FileInputStream(file);

            document = new XWPFDocument(in);

            List<XWPFParagraph> paragraphs = document.getParagraphs();

            handleParagraphs(document,map);

            //setContentType:设置响应内容类型。这表示是一个pdf文件

            response.setContentType("application/msword");

            //setCharacterEncoding:设置字符集。

            response.setCharacterEncoding("utf-8");

            //设置表名

            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);



            OutputStream out = null;

            out=response.getOutputStream();



            ByteArrayOutputStream bos = new ByteArrayOutputStream();

//            OutputStream out = new FileOutputStream(fileName);

            document.write(bos);

            out.write(bos.toByteArray());

            response.getOutputStream();

            bos.close();

            out.close();



            return true;

        }

        catch (Exception e){

            e.printStackTrace();

            return false;

        } finally{

            try

            {

                if ( document != null )

                {

                    document.close();

                }

            }

            catch (IOException e)

            {

                e.printStackTrace();

            }

        }

    }









    /**

     * @param wordValue ${...} 带${}的变量

     * @param map       存储需要替换的数据

     * @return java.lang.String

     * @Description 有${}的值匹配出替换的数据,没有${contractName}就返回原来的数据

     * @author hacah

     * @Date 2021/6/15 16:02

     */

    public static String matchesValue(String wordValue, Map<String, String> map) {

        for (String s : map.keySet()) {

            String s1 = new StringBuilder("${").append(s).append("}").toString();

            if (s1.equals(wordValue)) {

                wordValue = map.get(s);

            }

        }

        return wordValue;

    }



    /**

     * @return boolean

     * @Description 测试是否包含需要替换的数据

     * @author hacah

     * @Date 2021/6/15 15:30

     */

    public static boolean isReplacement(String text) {

        boolean check = false;

        if (text.contains("$")) {

            check = true;

        }

        return check;

    }



    /**

     * @Description 处理所有文段数据,除了表格

     * @param xwpfDocument

     * @param insertTextMap

     * @author hacah

     * @Date 2021/6/17 10:04

     */

    public static void handleParagraphs(XWPFDocument xwpfDocument, Map<String, String> insertTextMap) {

        for (XWPFParagraph paragraph : xwpfDocument.getParagraphs()) {

            String text = paragraph.getText();

            if (isReplacement(text)) {

                for (XWPFRun run : paragraph.getRuns()) {

                    // 判断带有${}的run

                    // System.out.println(run);

                    run.setText(matchesValue(run.text(), insertTextMap), 0);

                }

            }

        }



    }







    /**

     * 替换段落里面的变量

     * @param paragraph

     * @param map

     */

    public static void replaceParagraph(XWPFParagraph paragraph, Map<String,Object> map){

        List<XWPFRun> runs = paragraph.getRuns();

        for (int i = 0; i < runs.size(); i++) {

            XWPFRun run = runs.get(i);

            String tkey = run.toString();

            if(tkey==null){

                return;

            }

            for (String key : map.keySet()) {

                if(tkey.equals(key)){

                    int size = run.getFontSize();

                    if(size==-1){

                        size=14;

                    }

                    String fontFamily = run.getFontFamily();

                    //因为直接调用setText()方法替换文本时,会在底层重新创建一个run,所以在设置文本之前要先删除当前run

                    paragraph.removeRun(i);

                    if(map!=null && map.get(key)!=null){

                        String runText = map.get(key).toString();

                        if(runText!=null){

                            if(runText.indexOf("\r\n")!=-1){

                                String[] texts = runText.split("\r\n");

                                List<String> tmp = new ArrayList<>();

                                for (String text : texts) {

                                    if(text!=null && text.length()!=0){

                                        tmp.add(text);

                                    }

                                }

                                texts = tmp.toArray(new String[0]);

                                for (int n = 0; n < texts.length; n++) {

                                    XWPFRun newRun = paragraph.createRun();

                                    newRun.setText("    "+texts[n].trim());

                                    if(texts.length>1 && n!=texts.length-1){

                                        newRun.addBreak();

                                    }

                                    newRun.setText(runText);

                                    newRun.setFontSize(size);

                                    newRun.setFontFamily(fontFamily);

                                }

                            }else if(runText.indexOf("\n")!=-1){

                                String[] texts = runText.split("\n");

                                List<String> tmp = new ArrayList<>();

                                for (String text : texts) {

                                    if(text!=null && text.length()!=0){

                                        tmp.add(text.trim());

                                    }

                                }

                                texts = tmp.toArray(new String[0]);

                                for (int n = 0; n < texts.length; n++) {

                                    XWPFRun newRun = paragraph.createRun();

                                    newRun.setText("    "+texts[n].trim());

                                    if(texts.length>1 && n!=texts.length-1){

                                        newRun.addBreak();

                                    }

                                    newRun.setFontSize(size);

                                    newRun.setFontFamily(fontFamily);

                                }

                            }else{

                                //重新创建一个run用于设置文本

                                XWPFRun newrun = paragraph.insertNewRun(i);

                                newrun.setText(runText);

                                newrun.setFontSize(size);

                                newrun.setFontFamily(fontFamily);

                            }

                        }

                    }

                }

            }

        }

    }





    /**

     * 替换表格里面的变量

     * @param document

     * @param map

     */

    public static void replaceInTable(XWPFDocument document,Map<String,Object> map) throws Exception {

        Iterator<XWPFTable> iterator = document.getTablesIterator();

        while (iterator.hasNext()){

            //获取表

            XWPFTable table = iterator.next();

            //获取行

            List<XWPFTableRow> rows = table.getRows();

            for (XWPFTableRow row : rows) {

                //获取单元格

                List<XWPFTableCell> cells = row.getTableCells();

                for (XWPFTableCell cell : cells) {

                    List<XWPFParagraph> paragraphs = cell.getParagraphs();

                    for (XWPFParagraph paragraph : paragraphs) {

                        //paragraph.setAlignment(ParagraphAlignment.LEFT);

                        replaceParagraph(paragraph,map);

                    }

                }

            }

        }

    }





    /**

     * 替换页眉里面的变量

     * @param Header

     * @param map

     */

    public static void replaceHeader(XWPFHeader Header,Map<String,Object> map){

        //获取表

        List<XWPFTable> tableList = Header.getTables();

        if(tableList!=null && tableList.size()>0){

            for (int i = 0; i < tableList.size(); i++) {

                XWPFTable table = tableList.get(i);

                //获取行

                List<XWPFTableRow> rows = table.getRows();

                for (XWPFTableRow row : rows) {

                    //获取单元格

                    List<XWPFTableCell> cells = row.getTableCells();

                    for (XWPFTableCell cell : cells) {

                        //获取单元格内容

                        List<XWPFParagraph> paragraphs = cell.getParagraphs();

                        for (XWPFParagraph paragraph : paragraphs) {

//                            paragraph.setAlignment(ParagraphAlignment.LEFT);

                            replaceParagraph(paragraph,map);

                        }

                    }

                }

            }

        }

        else {

            List<XWPFParagraph> paragraphs = Header.getParagraphs();

            for (XWPFParagraph paragraph : paragraphs) {

//                paragraph.setAlignment(ParagraphAlignment.LEFT);

                replaceParagraph(paragraph,map);

            }

        }



    }





}

以上代码大部分是copy一位大佬的。大佬看见请见谅。







    private static void replaceParagraph(XWPFParagraph para,LinkedHashMap<String,XmlObject> newParaMap){

        if(CollectionUtils.isNotEmpty(para.getRuns())) {

            //删除所有文本段

            while(para.removeRun(0)) {

            }

        }

        newParaMap.forEach((k,v)->{

            //创建新文本段,并恢复格式

            XWPFRun run = para.createRun();

            run.getCTR().set(v);

            run.setText(k,0);

        });

    }





    private static void replaceInParagraph(XWPFParagraph para, Map<String, String> parameterMap) {

        String paraText = para.getParagraphText();

        if(!paraText.contains("${") || !paraText.contains("}") ){

            return ;

        }

        log.info("paraText:【{}】",paraText);

        List<XWPFRun> xwpfRuns  = para.getRuns();

        StringBuilder keySb = new StringBuilder();

        boolean hasKey = false;

        LinkedHashMap<String, XmlObject> newParaMap = new LinkedHashMap<>();

        for(int i=0;i<xwpfRuns.size();i++){

            XWPFRun run = xwpfRuns.get(i);

            String str = run.text();

            int bIndex =0;

            int eIndex =0;

            do{

                int end = str.indexOf("}",eIndex);

                //已经有关键字内容

                if(hasKey){

                    if(end>=0) { //末尾

                        keySb.append(str.substring(bIndex,end));

                        eIndex = end + 1;

                        bIndex = eIndex;

                        String value = String.valueOf(parameterMap.getOrDefault(keySb.toString(),""));

                        //替换关键字内容

                        newParaMap.put(value,run.getCTR().copy());

                        keySb.setLength(0);

                        hasKey = false;

                        continue;

                    }else{

                        //有内容,但没有结束符,当前部分字符串作为关键字内容

                        keySb.append(str.substring(bIndex));

                        bIndex = str.length();

                        eIndex = bIndex;

                    }

                }else{

                    //没有关键字内容

                    int begin = str.indexOf("$",eIndex);

                    if(end>=0 && end<begin){

                        //修正无效的结束符

                        newParaMap.put(str.substring(bIndex,end+1),run.getCTR().copy());

                        eIndex = end + 1;

                        bIndex = eIndex;

                        continue;

                    }

                    //存在起始符

                    if(begin>=0){

                        int begin1 = str.indexOf("${",begin);

                        //存在关键字内容起始符

                        if(begin1>=0){

                            //保存关键字起始符

                            hasKey = true;

                            bIndex = begin1+2;

                            eIndex = bIndex ;

                            continue;

                        }else{

                            //不存在关键字内容起始符,

                            //$ 是当前字符串末尾且不是数组最后一个元素

                            if(begin==str.length()-1 && i+1<xwpfRuns.size()){

                                int begin2 = xwpfRuns.get(i+1).text().indexOf("{");

                                // { 必须是下一个字符串元素的首字母

                                if(begin2==0){

                                    hasKey = true;

                                    i++;

                                    run = xwpfRuns.get(i);

                                    str = run.text();

                                    bIndex=1;

                                    eIndex=1;

                                    continue;

                                }else{

                                    //下一个数组元素的第一个字符不是{

                                    newParaMap.put(str.substring(bIndex),run.getCTR().copy());

                                    break;

                                }

                            }else{

                                //不是最后一个字符或者是最后一个元素

                                newParaMap.put(str.substring(bIndex),run.getCTR().copy());

                                break;

                            }

                        }

                    }else{

                        //不存在起始符

                        newParaMap.put(str.substring(bIndex),run.getCTR().copy());

                        break;

                    }

                }

            }while(bIndex<str.length());

        }



        replaceParagraph(para,newParaMap);

        log.info("replaced  paraText:【{}】",para.getParagraphText());

    }



以上代码完全是copy另一位大佬的。但是此段代码有个问题处理不了。

文字加${xxx},文字很少且在同一行时,这段代码的处理是吧文字和${}去掉留下需要替换的内容。

我的处理方式是:把“文字加${xxx}”封装在一个string中把整体看做一个占位符,进行替换。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值