1

产品提了个这样的需求:分别生成一个word文档和pdf文档,里面需要包含数据,这些数据需要有对应的样式比如字体大小,表格,段落等。开始的想法是用Apache POI,但看了那一大段填充值操作的代码发现太过冗长,决定采用Freemarker模版的方案。
百度了一圈看上去Spring Boot + FreeMarker制作word模板导出Word表格这种做法像那么一回事,尝试了一波后发现生成的是只能生成doc格式的文件!只能生成doc格式的文件!只能生成doc格式的文件!我要的是docx格式的呀哥???
迫不得已我又只能经过漫长的搜索尝试失败再搜索尝试失败.....好吧,终于找到生成docx的方案:
1.将docx后缀修改为zip,取出其中的document.xml文档以及_rels文件夹下的document.xml.rels文档。
document.xml是填充文本类内容需要用用到的文件,比如你可以这样修改
1570758777704.jpg
意思是在生成新的docx时,将这个${}内容替换成我们传进来的参数,至于模版怎么整理,还有freemarker的循环,判断这些语法就自行百度吧,easy!
document.xml.rels是你的docx中需要替换其中的图片时要用到的文件,比如我将原来的

<Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image1.jpeg"/>

替换成了

<Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/${img.imgTargetName}"/>

意思是我将image1.jpeg替换成了${img.imgTargetName}这个变量对应的图片,当然zip包里的media文件夹也需要将新的图片文件写进去,需要注意的是,新文件的后缀名最好与原来文件的后缀名一致,否则生成的docx可能打不开。
2.读取zip文件的压缩流,将原来的document.xml、document.xml.rel文件替换成填充过内容的的 document.xml、document.xml.rels文件,把图片写入zip文件下word/media文件夹中
输出docx文档。
上代码

    public static String exportImgWordDocx(Map map,String wordXmlName,String wordImgXmlResName)
            throws Exception {
        Template freemarkerTemplate = configuration.getTemplate(wordXmlName);
        Template imgFreemarkerTemplate = configuration.getTemplate(wordImgXmlResName);
        InputStream fin = null;
        ServletOutputStream out = null;
        Writer writerOut = null;
        Writer writerOutImg = null;
        File outdocxXmlFile =null;
        File outdocxImgXmlFile = null;
        File docxFile = null;
        String docxPath = null;
        try {
            //指定输出word docx的数据文件的路径
            String outdocxXmlFilePath = exportResourcePath+UUID.randomUUID().toString()+".xml";
            outdocxXmlFile = new File(outdocxXmlFilePath);
            FileOutputStream fos = new FileOutputStream(outdocxXmlFile);
            writerOut = new BufferedWriter(new OutputStreamWriter(fos),10240);
            //将map数据写入到xml模板中,获取docx数据文件
            freemarkerTemplate.process(map,writerOut);


            //指定输出word docx的图片数据文件的路径
            String outdocxImgXmlFilePath = exportResourcePath+UUID.randomUUID().toString()+".xml.rels";
            outdocxImgXmlFile = new File(outdocxImgXmlFilePath);
            FileOutputStream fosImg = new FileOutputStream(outdocxImgXmlFile);
            writerOutImg = new BufferedWriter(new OutputStreamWriter(fosImg),10240);
            //将map数据写入到图片xml模板中,获取docx图片数据文件
            imgFreemarkerTemplate.process(map,writerOutImg);


            //将docx数据xml文件、图片数据xml文件替换到对应的docx实例zip压缩包中去
            ClassPathResource resource = new ClassPathResource("templates/SummaryTemplate.zip");
            InputStream zipFileInputStream = resource.getInputStream();

            docxPath = exportResourcePath +UUID.randomUUID().toString()+".docx";
            docxFile = new File(docxPath);

            ZipInputStream zipInputStream = ZipUtils.wrapZipInputStream(zipFileInputStream);
            ZipOutputStream zipOutputStream = ZipUtils.wrapZipOutputStream(new FileOutputStream(docxFile));
            String itemNameOne = "word/document.xml";
            String itemNameTwo = "word/_rels/document.xml.rels";
            List<Map<String,String>> imgList = (List<Map<String,String>>)map.get("imgList");
            ZipUtils.replaceTwoItem(zipInputStream, zipOutputStream, itemNameOne, new FileInputStream(outdocxXmlFile),itemNameTwo,new FileInputStream(outdocxImgXmlFile),imgList);


        } catch (TemplateException e) {
            StackTraceElement[] stackTraceElements = e.getStackTrace();
            logger.error(e.toString() + "---" + stackTraceElements[0].toString());
            throw new SystemRunException(e.toString() + "---" + stackTraceElements[0].toString());
        }finally {
            if (out != null) {
                out.close();
            }
            if (fin != null) {
                fin.close();
            }
            if(writerOut != null){
                writerOut.close();
            }
            if(writerOutImg != null){
                writerOutImg.close();
            }
            if(outdocxXmlFile!=null){
                outdocxXmlFile.delete();//删除docx临时数据文件
            }
            if(outdocxImgXmlFile!=null){
                //outdocxImgXmlFile.delete();//删除docx临时图片数据文件
            }
            if(docxFile!=null){
                //docxFile.delete();//删除docx临时文件
            }
        }
        return docxPath;
    }

如果需要生成pdf格式文件,也是需要先生成docx格式文件再转换成pdf格式文件,至于这个转换的过程,我找的到方案里最简单靠谱的还是用asponse-words,但这是个收费软件,csdn里很多破解版的jar下载资源,不使用破解版的话生成的pdf文件是会有水印的。
上代码:

public static void asposeWord2Pdf(String docxPath,String pdfPath){
        logger.info("docxPath" + docxPath + "pdfPath" + pdfPath);

        if (!getLicense()) {
            return;
        }
        FileOutputStream os =null;
        try {
            File file = new File(pdfPath); // 新建一个空白pdf文档
            os = new FileOutputStream(file);
            Document doc = new Document(docxPath); // Address是将要被转化的word文档
            doc.save(os, SaveFormat.PDF);
        } catch (Exception e) {
            StackTraceElement[] stackTraceElements = e.getStackTrace();
            logger.error(e.toString() + "---" + stackTraceElements[0].toString());
            throw new SystemRunException(e.toString() + "---" + stackTraceElements[0].toString());
        }finally{
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

后面可能还会遇到SpringBoot引用外部jar的问题,生成的Pdf文件里都是小方块这样的问题,生成文件慢各种各样的问题,都需要一一耐心解决,别问我怎么知道的....哭!

以下都是大佬:

java利用Freemarker模板生成docx格式的word文档

java利用Freemarker模板生成格式友好的doc或者docx文档

freemarker基于docx格式创建模板导出带图片pdf文件


WillLiaowh
71 声望8 粉丝

世界上最伟大的力量是坚持。