首页 > 其他分享 >使用POI填充Word文档,一些注意事项以及解决办法

使用POI填充Word文档,一些注意事项以及解决办法

时间:2024-04-06 23:34:01浏览次数:27  
标签:docx Word String templateFilePath POI 文档 doc new response

有这样一个需求:需要将用户输入的数据填写到准备好的Word模版中并提供下载,最终选择POI-tl和POI来完成上述需求。

在这个过程中,主要遇到了以下两个问题:

1. Word的两个格式doc和docx(两种文件的区别大家可以自行百度了解下),POI并没有提供统一的处理类。分别用 HWPFDocument 处理doc文档,用 XWPFTemplate 处理docx文档。

2. 在用 HWPFDocument 处理doc文档时,如果遇到一些复杂的表格(像是有合并的单元格时),会导致一读取doc文档,生成的doc文档就无法打开,一般打开时,word会报如下错误:

(这里不知道是为什么,如果有大佬知道原因,希望可以指教下)。而将文件模版转换为docx后,可以正常填充。

所以这里采取的解决办法是:先将文件模版转换为docx,填充好后,再通过POI转换为doc。(其中docx转换为doc的代码看了好多都没有好用的,试了下将在输入输出流进行读取时候,将文件名换一下,居然好用了,然而我并没有搞明白是为什么,同样希望可以请教下大佬。)

下面是填充Word并提供下载的完整代码:

/**
*  Word模版填充工具类
*/
public class WordUtil {

    /**
     * .doc和.docx类型的文件需要分别进行处理,
     * 同时由于无法正确读取部分doc文档,采用先进行docx填充,再转换为doc方案
     */
    public static void fillAndDownloadWordDocument(String templateFilePath, Map<String, String> dataMap, HttpServletResponse response, String cacheFilePath) {
        // 判断文件类型,根据不同类填充
        if (templateFilePath.endsWith(".docx") && templateFilePath.contains("复杂表格模板")) {
            fillAndConvertDocxToDoc(templateFilePath, dataMap, cacheFilePath, response);
        } else if (templateFilePath.endsWith(".docx")) {
            fillAndDownloadDocx(templateFilePath, dataMap, response);
        } else if (templateFilePath.endsWith(".doc")) {
            fillAndDownloadDoc(templateFilePath, dataMap, response);
        } else {
            throw new RuntimeException("文件类型错误");
        }
    }

    /**
     * 将数据填充到对应word模版中,并提供下载docx类型文件
     *
     * @param templateFilePath 文件名称
     * @param dataMap        填充word模板中的数据
     */
    private static void fillAndDownloadDocx(String templateFilePath, Map<String, String> dataMap, HttpServletResponse response) {
        ConfigureBuilder builder = Configure.newBuilder();
        builder.buildGramer("${", "}");
        XWPFTemplate template;

        try {
            template = XWPFTemplate.compile(new FileInputStream(templateFilePath), builder.build()).render(dataMap);
        } catch (IOException e) {
            throw new RuntimeException("文件模版填充失败");
        }

        if (template == null) {
            throw new RuntimeException("文件模版填充失败");
        }

        //浏览器单个下载生成的word文件
        // 文件名 日期-文件名
        StringBuffer resultDocName = new StringBuffer();
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HHmmss");
        resultDocName.append(dateFormat.format(date)).append(templateFilePath.substring(templateFilePath.lastIndexOf("\\") + 1));
        String exportFileName = resultDocName.toString();

        ServletOutputStream outputStream = null;
        // 进行文件下载
        try {
            outputStream = response.getOutputStream();
            // 对文件名进行编码处理中文问题
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(exportFileName, "UTF-8"));
            response.setCharacterEncoding("UTF-8");
            template.write(outputStream);
        } catch (Exception e) {
            throw new RuntimeException("文件下载失败");
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
                template.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将数据填充到对应word模版中,并提供下载doc类型文件
     *
     * @param templateFilePath 导入文件路径
     * @param dataMap        填充word模板中的数据
     */
    private static void fillAndDownloadDoc(String templateFilePath, Map<String, String> dataMap, HttpServletResponse response) {
        HWPFDocument doc;
        try {
            doc = new HWPFDocument(new FileInputStream(templateFilePath));
        } catch (IOException e) {
            throw new RuntimeException("加载word文件模版失败");
        }
        // 填充内容
        Range range = doc.getRange();
        for (Map.Entry<String, String> entry : dataMap.entrySet()) {
            // 这里需要判空,poi内部会调用value.length
            String value = entry.getValue();
            if (value == null) {
                value = "";
            }
            range.replaceText(build(entry.getKey()), value);
        }

        // 填充页眉
        Range headerStoryRange = doc.getHeaderStoryRange();
        for (Map.Entry<String, String> entry : dataMap.entrySet()) {
            String value = entry.getValue();
            if (value == null) {
                value = "";
            }
            headerStoryRange.replaceText(build(entry.getKey()), value);
        }

        //浏览器单个下载生成的word文件
        // 文件名 日期-文件名
        StringBuffer resultDocName = new StringBuffer();
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HHmmss");
        resultDocName.append(dateFormat.format(date)).append(templateFilePath.substring(templateFilePath.lastIndexOf("\\") + 1));
        String exportFileName = resultDocName.toString();

        ServletOutputStream outputStream = null;
        // 进行文件下载
        try {
            outputStream = response.getOutputStream();
            // 对文件名进行编码处理中文问题
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(exportFileName, "UTF-8"));
            response.setCharacterEncoding("UTF-8");
            doc.write(outputStream);
        } catch (Exception e) {
            throw new RuntimeException("文件下载失败");
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
                doc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static String build(String s) {
        return "${" + s + "}";
    }

    /**
     * 由于部分doc文档,poi无法直接读取,采用以下方案进行
     * 1.外界将模版保存为docx,进行填充
     * 2.填充后docx保存至本地,转换为doc
     * 3.提供下载,删除过程中产生的文件
     *
     * @param templateFilePath 模版文件位置
     * @param dataMap        填充数据
     * @param cacheFilePath  文件缓存目录
     */
    private static void fillAndConvertDocxToDoc(String templateFilePath, Map<String, String> dataMap, String cacheFilePath, HttpServletResponse response) {
        ConfigureBuilder builder = Configure.newBuilder();
        builder.buildGramer("${", "}");
        XWPFTemplate template;

        try {
            template = XWPFTemplate.compile(new FileInputStream(templateFilePath), builder.build()).render(dataMap);
        } catch (IOException e) {
            throw new RuntimeException("文件模版填充失败");
        }

        if (template == null) {
            throw new RuntimeException("文件模版填充失败");
        }

        //浏览器单个下载生成的word文件
        // 文件名 日期-文件名
        StringBuffer resultDocName = new StringBuffer();
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HHmmss");
        resultDocName.append(dateFormat.format(date)).append(templateFilePath.substring(templateFilePath.lastIndexOf("\\") + 1));
        String exportFileName = resultDocName.toString();

        String exportFilePath = cacheFilePath + exportFileName;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(exportFilePath);
            template.write(fos);
        } catch (IOException e) {
            System.out.println(exportFileName + "文件保存失败");
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
                template.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 填充完毕后,进行docx转doc,并提供下载
        String docName = exportFileName.replaceFirst(".docx", ".doc");
        String docPath = cacheFilePath + docName;
        convertDocxToDoc(exportFilePath, docPath);
        downloadWord(response, docName, docPath);
        deleteFile(exportFilePath);
        deleteFile(docPath);
    }

    /**
     * 上传文件
     *
     * @param response 响应
     * @param filePath 文件路径
     */
    private static void downloadWord(HttpServletResponse response, String exportFileName, String filePath) {
        FileInputStream fis = null;
        ServletOutputStream outputStream = null;
        try {
            fis = new FileInputStream(filePath);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(exportFileName, "UTF-8"));
            response.setCharacterEncoding("UTF-8");
            outputStream = response.getOutputStream();
            byte[] bytes = new byte[1024];
            int readCount;
            while ((readCount = fis.read(bytes)) != -1) {
                outputStream.write(bytes, 0, readCount);
            }
            fis.close();
            outputStream.close();
        } catch (IOException e) {
            System.out.println(filePath + "文件上传失败");
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * docx文档转换为doc文档
     *
     * @param docxPath docx路径
     * @param docPath  doc路径
     */
    private static void convertDocxToDoc(String docxPath, String docPath) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(docxPath);
            XWPFDocument docx = new XWPFDocument(fis);
            fos = new FileOutputStream(docPath);
            docx.write(fos);

        } catch (IOException e) {
            System.out.println(docxPath + "文件转换失败");
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 删除文件
     *
     * @param filePath 文件路径
     */
    private static void deleteFile(String filePath) {
        File file = new File(filePath);
        if (file.exists() && file.isFile()) {
            try {
                boolean delete = file.delete();
                if (!delete) {
                    System.out.println("文件删除失败");
                }
            } catch (Exception e) {
                System.out.println("文件删除失败");
            }
        }
    }
}

我这里用的依赖是比较老的依赖了,其他版本api名字可能有不同,当然也要注意下不同版本的poi的依赖之间可能会有冲突。所用依赖如下:

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>4.0.0</version>
        </dependency>

有错误还请各位兄弟指出

标签:docx,Word,String,templateFilePath,POI,文档,doc,new,response
From: https://blog.csdn.net/m0_63400588/article/details/137440297

相关文章

  • Word中字体间距变大且无法修改字体
    Word中字体间距变大且无法修改字体问题描述今天在Word中写文档的时候不知怎么触发了输入法全角半角设置快捷键,在Word中输入的英文字体间距很大,起初我以为是字体问题,然而修改字体后无任何变化,仔细研究一番后注意到输入法英文状态下为全角输入,这才找到问题所在,如下图所示全......
  • Cisco ISO升级文档
    第一步查看flash是否有足够的空间Switch#dir第二步将U盘插入交换机USB接口*Sep1310:26:48.686:%IOSD_INFRA-6-IFS_DEVICE_OIR:Deviceusbflash0addedSwitch#copyusbflash0:cat9k_lite_iosxe.17.06.04.SPA.binflash:第三步将U盘中的cat9k_lite_iosxe.17.06.04.SPA......
  • opencv的projectPoints的使用
    前言今天要用到重投影这块的内容,是想把相机坐标系下的一个点投影到像素坐标系下。原理很简单,其实手搓一个函数也完全可以。但是还是想尝试一下用封装好的库函数,这样比较简洁和优雅。但是使用过程中遇到了一些程序死掉的问题。代码片段std::vector<cv::Point3f>objectPoints......
  • 【包远程安装运行】SpringBoot+Mysql实现的在线兼职实习招聘管理系统源码+运行教程+开
    今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的在线招聘系统,主要实现了在线招聘基本操作流程的全部功能,系统分普通用户、管理员等角色,除基础脚手架外,实现的功能有:管理员:系统管理、职位管理、新闻咨询管理、简历投递管理、在线统计等。普通用户(......
  • 【包远程安装运行】:SpringBoot+Mysql健身房在线预约管理系统源码+运行视频+开发文档(参
    今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的健身房在线预约管理系统,系统分四个角色,管理员,职工、教练、前台用户,各角色功能如下:管理员:系统管理(角色、权限、菜单等)、职工管理、健身会员管理、会员充值管理、健身项目管理、健身百科管理、健身......
  • [StartingPoint][Tier1]Bike
    Task1WhatTCPportsdoesnmapidentifyasopen?Answerwithalistofportsseperatedbycommaswithnospaces,fromlowtohigh.(nmap识别哪些TCP端口为开放?回答以逗号分隔的端口列表,不带空格,从低到高。)#nmap-sS-T4-Pn10.129.97.64--min-rate1000......
  • 【MATLAB源码-第170期】基于matlab的BP神经网络股票价格预测GUI界面附带详细文档说明
    操作环境:MATLAB2022a1、算法描述基于BP神经网络的股票价格预测是一种利用人工神经网络中的反向传播(Backpropagation,简称BP)算法来预测股票市场价格变化的技术。这种方法通过模拟人脑的处理方式,尝试捕捉股票市场中的复杂非线性关系,以实现对未来股价的预测。本文将详细介绍BP......
  • java计算机毕业设计(附源码)原创网络文学管理系统(ssm+mysql+maven+LW文档)
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义标题:原创网络文学管理系统的开发背景在数字化时代背景下,网络文学作为一种新兴的文学形式,以其便捷、互动性强的特点迅速崛起,成为文化消费的重要领域。随着网络文学......
  • java计算机毕业设计(附源码)远程家庭健康监测管理系统(ssm+mysql+maven+LW文档)
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义标题:远程家庭健康监测管理系统的选题背景与意义随着现代科技的迅猛发展,人们对于健康管理的关注也日益增加。在这样的背景下,远程家庭健康监测管理系统应运而生,成为......
  • CF1200E Compress Words 题解
    题目链接:CF或者洛谷注意到总字符串长度不超过\(1e6\),对于两个串之间找前后缀匹配,只要能暴力枚举长度,\(check\为\O(1)\),那么最后显然线性复杂度。可以考虑\(kmp\),也可以考虑字符串哈希,最好上双哈希,然后拼串显然是在尾部继续添加新的前缀哈希,这个需要添加的串可以单独由匹配......