java使用freemarker导出word(xlsx格式,含图表)

使用java程序导出word,此次使用的是模板值替换方式,而导出的word版本是xlsx。

直接替换文本和图片,可以直接使用一个word改造的ftl模板,替换启用的参数即可,但是对于图表就比较麻烦。

也参照了很多大佬的做法,针对xlsx07版本,主要是将xlsx文件解压,得到很多xml文件,其中文字内容是在document.xml中,如果要替换文本内容,将xml中参数使用freemarker替换即可。

如果是图片,则按照图片的先后顺序,保存在word/media/下,以image1.png这个格式开头,则在最后写入压缩包时替换相应的图片。

图表也就和文本document.xml的方式一样,word中的每个图表都会有一个chart[1].xml文件,将这个xml中的参数使用freemarker替换。

 

下面就是一个导出word,含图表的工具类,供参考

其中的几个DTO就是关于模板文件名称和参数。

public class WordWriter {


    public static void write(String outFileName, WordTemplate wordTemplate, boolean zip, HttpServletResponse httpServletResponse){

        //获取临时文件存放路径
        outFileName = getWordFileName(outFileName);
        File file = FileUtil.createFile(outFileName);
        //创建xml临时存放文件夹
        Path parent = file.toPath().getParent();
        File xmlDir = Paths.get(parent.toString(), ExportConstant.xmlDirName).toFile();
        if(!xmlDir.exists()){
            boolean mkdir = xmlDir.mkdir();
            log.debug("创建xml临时存放文件夹{}", mkdir);
        }
        //创建document.xml
        WordXmlTemplateDTO documentDTO = wordTemplate.getDocumentDTO();
        Path documentXmlOutPath = appendXmlPath(xmlDir, "document.xml");
        FreemarkerUtils.process(documentDTO.getXmlName(), documentDTO.getDataList(), documentXmlOutPath.toString());

        //替换chart.xml
        List<WordXmlTemplateDTO> chartDTOList = wordTemplate.getChartDTOList();
        List<Path> chartXmlPaths = new ArrayList<>();
        if(!CollectionUtils.isEmpty(chartDTOList)){
            int i = 1;
            for (WordXmlTemplateDTO chartDTO : chartDTOList) {
                Path chartXmlOutPath = appendXmlPath(xmlDir, "chart" + i++ + ".xml");
                FreemarkerUtils.process(chartDTO.getXmlName(), chartDTO.getDataList(), chartXmlOutPath.toString());
                chartXmlPaths.add(chartXmlOutPath);
            }
        }

        //写入
        Path templatePath = FreemarkerUtils.getTemplatePath();
        Path originDocPath = Paths.get(templatePath.toString(), wordTemplate.getOriginDocName());
        Path outDocPath = Paths.get(xmlDir.getPath(), outFileName);
        replaceDocXml(documentXmlOutPath, wordTemplate.getWordImageDTO(), chartXmlPaths, originDocPath, outDocPath);

        Path zipPath = null;
        if(zip){
            zipPath = FileUtil.zip(Lists.newArrayList(outDocPath.toFile()), outFileName);
        }

        if(httpServletResponse != null){
            httpServletResponse.reset();
            try (OutputStream outputStream = httpServletResponse.getOutputStream();){
                if(zipPath != null){
                    httpServletResponse.setHeader("Content-Disposition",
                            "attachment;filename=" + URLEncoder.encode(zipPath.toFile().getName(), "utf-8"));
                    httpServletResponse.setHeader("Content-Type", "application/octet-stream");
                    Files.copy(zipPath, outputStream);
                }else {

                    httpServletResponse.setHeader("Content-Disposition",
                            "attachment;filename=" + URLEncoder.encode(outDocPath.toFile().getName(), "utf-8"));
                    httpServletResponse.setHeader("Content-Type", "application/msword");
                    Files.copy(outDocPath, outputStream);
                }
            } catch (Exception e) {
                log.error("导出文件失败" + outDocPath, e);
            }
        }else {
            log.error("web导出Word,httpResponse为空");
        }
        log.debug(outDocPath.toString());
        FileUtil.deleteAll(file.toPath().getParent());

    }

    private static void replaceDocXml(Path documentXmlOutPath, List<WordImageDTO> wordImageDTOList, List<Path> chartXmlPaths, Path originDocPath, Path outDocPath)  {

        int len;
        byte[] buffer=new byte[1024];

        try (ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(outDocPath.toFile()))){

            ZipFile zipFile = new ZipFile(originDocPath.toFile());
            Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();

            while(zipEntrys.hasMoreElements()) {
                ZipEntry next = zipEntrys.nextElement();
                String name = next.toString();
                zipout.putNextEntry(new ZipEntry(name));

                int chartIndex;
                if ("word/document.xml".equals(name)) {

                    try (InputStream in = new FileInputStream(documentXmlOutPath.toFile())){
                        while ((len = in.read(buffer)) != -1) {
                            zipout.write(buffer, 0, len);
                        }
                    }

                } else if (!CollectionUtils.isEmpty(wordImageDTOList)
                        && name.startsWith("word/media/")
                        && getImageIndex(name) > 0) {

                    int lastIndexOf = name.lastIndexOf("/");
                    String imageName = name.substring(lastIndexOf + 1);
                    boolean flag = false;
                    for (WordImageDTO wordImageDTO : wordImageDTOList) {
                        byte[] bytes = wordImageDTO.getBytes();
                        String docxImageName = wordImageDTO.getDocxImageName();
                        if(imageName.equalsIgnoreCase(docxImageName)){
                            zipout.write(bytes);
                            flag = true;
                            break;
                        }
                    }
                    if(!flag){
                        originWrite(buffer, zipout, zipFile, next);
                    }

                }else if(!CollectionUtils.isEmpty(chartXmlPaths)
                        && name.startsWith("word/charts/")
                        && (chartIndex = getChartIndex(name)) > 0
                        && chartXmlPaths.size() >= chartIndex){

                    Path path = chartXmlPaths.get(chartIndex - 1);
                    try (InputStream in = new FileInputStream(path.toFile())){
                        while ((len = in.read(buffer)) != -1) {
                            zipout.write(buffer, 0, len);
                        }
                    }

                }else {
                    originWrite(buffer, zipout, zipFile, next);
                }
                zipout.flush();
            }
        } catch (Exception e) {
            log.error("写入docx异常", e);
        }
    }


    private static void originWrite(byte[] buffer, ZipOutputStream zipout, ZipFile zipFile, ZipEntry next) throws IOException {
        int len;
        try (InputStream is = zipFile.getInputStream(next)){
            while((len = is.read(buffer))!=-1){
                zipout.write(buffer,0,len);
            }
        }
    }

    /**
     * docx解压包中,图片名称是按照image[1-9].[png|...]规律命名的
     */
    private static int getImageIndex(String name){
        int index = -1;
        String reg = ".*image([0-9]+)\\.\\w+$";
        Pattern pattern = Pattern.compile(reg);
        Matcher matcher = pattern.matcher(name);
        if (matcher.find()) {
            index = Integer.valueOf(matcher.group(1));
        }
        return index;
    }

    /**
     * docx解压包中,图表xml文件是按照chart[1-9].xml规律命名的
     */
    private static int getChartIndex(String name){
        int index = -1;
        String reg = ".*chart([0-9]+).xml$";
        Pattern pattern =Pattern.compile(reg);
        Matcher matcher = pattern.matcher(name);
        if (matcher.find()) {
            index = Integer.valueOf(matcher.group(1));
        }
        return index;
    }

    private static Path appendXmlPath(File xmlDir, String fileName){
        if(!fileName.endsWith(".xml")){
            fileName = fileName + ".xml";
        }
        return Paths.get(xmlDir.toPath().toString(), fileName);
    }

    private static String getWordFileName(String fileName){
        Assert.isTrue(fileName != null && fileName.trim().length() > 0, "导出文件名称不能为空");
        if(!fileName.endsWith(ExportFileType.WORD.getSuffix())){
            fileName = fileName + ExportFileType.WORD.getSuffix();
        }
        return fileName;
    }

}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值