1.目的:项目中遇到一个word导出的功能,需要在固定的模板里把一些字段替换掉,然后返回给前端,然后就实现

2.准备文件

a.将word文档需要填充的字段用占位字段替换${key},和map的key对应

java填充动态word包含表格 java填充word模板_xml


b.将word打开然后另存为xml文件,直接修改文件的后缀xml改为ftl,这样就生成想要的模板文件啦。

c.我是放入项目文件下的

java填充动态word包含表格 java填充word模板_xml_02


3.代码实现

package com.example.demo.service.impl;

import com.example.demo.service.WordService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.Version;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

@Service
public class WordServiceImpl implements WordService {
    @Override
    public String changWord(String id, HttpServletResponse httpServletResponse) {
        //用于区别不同请求的
        if (id == null) {
            id=String.valueOf(System.currentTimeMillis());
        }
        Map<String,Object> dataMap = new HashMap<String, Object>();
        try {
            //模板的参数,其中map各个字段必须要存在key,value可以为空
            dataMap.put("date","2020-06-07");
            dataMap.put("money","200");
            dataMap.put("author","taotao");
            dataMap.put("tie","测试文件");
            Configuration configuration = new Configuration(new Version("2.3.0"));
            configuration.setDefaultEncoding("utf-8");
            dataMap.put("data", "test");
            configuration.setDirectoryForTemplateLoading(new File("./config"));
            //输出文档路径及名称
            File outFile = new File("./config/test"+id+".doc");
            //以utf-8的编码读取ftl文件,模板的位置
            Template template = configuration.getTemplate("test.ftl", "utf-8");
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"), 10240);
            template.process(dataMap, out);
            out.close();
            try {
                FileInputStream fileInputStream = new FileInputStream(outFile);
                httpServletResponse.setCharacterEncoding("UTF-8");

                httpServletResponse.setHeader("Content-Disposition","attachment;filename=test"+id+".doc");
                ServletOutputStream outputStream = httpServletResponse.getOutputStream();
                byte[] data= new byte[1024];
                //浏览器直接下载
                int len;
                while ((len=fileInputStream.read(data))>0){
                    outputStream.write(data,0,len);
                }
                outputStream.close();
                fileInputStream.close();
                //字符流返回给前端
                StringBuffer stringBuffer = new StringBuffer();
                for (int i = 0; i <data.length ; i++) {
                    stringBuffer.append((char)data[i]);
                }
                //base64加密返回给前段
                String s = new Base64().encodeAsString(data);
                //用于文件删除
                outFile.delete();
                    //字节流返回
           //     return new String(stringBuffer);
            } catch (Exception e) {
                e.printStackTrace();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }


        return null;
    }
}

4.测试
a.在浏览器中输出
localhost:8090/word/test?id=32312就可以看到下载的文件了。
b.对于postman来说张这样

java填充动态word包含表格 java填充word模板_java填充动态word包含表格_03

然后点击download就行。

c.对于字符流返回的是一个string串然后你可以

java填充动态word包含表格 java填充word模板_xml_04


点击download,他会默认存一个txt把它另存为doc,然后效果是一模一样的。

总结:word输出比较简单主要是生成模板,然后是要输出直接下载的还是base加密的还是字符串就看自己了,在实际操作中word页数比较多,然后输出base64的字段较多,此时vue解密时,ie竟然不能获取全字符串,而chorm是可以的,这个前端问题还没解决,所以我目前用的是字符串而非base64的方式。

方案二:

直接通过poi进行字段的替换,为啥这样做呢,因为在实际的使用中竟然还有人在使用低版本的wps,你敢信???帮政府做的word居然乱码,但是在我们这试都好好的,就开始排查问题呀。

大概用了一整天查这个问题,首先复现问题,问他们要了一份他们的版本(版本号:8.1.0.3000),然后我们的是(9.1.0.5024),只能把本地的wps卸载了然后再装他们版本的wps。我敲真的乱码,打开张这样和返回结果一样的,

java填充动态word包含表格 java填充word模板_xml_05

为啥呢,再看下wps的升级,搜索xml,我去竟然在9.1.0.4940的版本新增了对xml框架的支持,我敲就是这之前的都不行啊。ps:word都可以就是低版本的wps妖路啊。

哎,看来xml作为模板的路是不行的了,没法只能另起炉灶了。

2.用啥方法捏?

最终只能用比较笨的方法了,poi来操作doc对象然后对字符串进行替换。

1.创建模板doc文件

java填充动态word包含表格 java填充word模板_java填充动态word包含表格_06

2.引入依赖

<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-scratchpad</artifactId>
			<version>3.9</version>
		</dependency>

3.代码实现

public String getWordByDoc(String id, HttpServletResponse httpServletResponse) {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("aa", "123");
        dataMap.put("bb", "还不错");
        dataMap.put("cc", 1);
        dataMap.put("date", "2020-07-27");
        try {
            //模板文件
            FileInputStream fileInputStream = new FileInputStream("./config/1.doc");
            HWPFDocument hwpfDocument = new HWPFDocument(fileInputStream);
            Range range = hwpfDocument.getRange();
            for (Map.Entry<String, Object> entry : dataMap.entrySet()
            ) {
                range.replaceText("${" + entry.getKey() + "}", entry.getValue().toString());
            }
            //生成的文件
            File file = new File("./config/newDoc.doc");
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            hwpfDocument.write(byteArrayOutputStream);
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(byteArrayOutputStream.toByteArray());
            fileInputStream.close();
            fileOutputStream.close();
            byte[] bytes = FileUtils.readFileToByteArray(file);
            return new String(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

4.测试

允许代码生成了doc

java填充动态word包含表格 java填充word模板_字段_07


ok,大功告成。

java填充动态word包含表格 java填充word模板_java填充动态word包含表格_08

两种方案的对比
1.flt方法比较智能,生成的是xml;doc能支持低版本的wps,但是用的是遍历不太行。