背景:根据word模板下载word。word里面需要填充数据,格式是固定的。首先在word中给数据起个变量名称
需要更换的数据改成${变量名!},必须是英文的,加感叹号是为了防止null,如果数据是null,文档下载下来后会直接显示null,加上感叹号就不会,没数据就是空。也可以不加感叹号,在代码设置下属性即可,后面会说到。变量名结束后把word另存为xml
然后复制到项目中,我用的是idea,打开后会发现就几行,后面老长了
然后format下ctrl+alt+L(快捷键仅供参考,本人是这样的),就会
这些红色不影响,不用管它。在文件中搜索${,被搜到的都是起变量的地方,有的地方是会分离的
这就需要手动帮他们破镜重圆了,剪切下即可
这种应该比较多,手动改下就行。改完后保存,改为.ftl文件。下面是代码
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;import java.io.*;
import java.util.Map;public class ExportController {public void download(WeeklyTask weeklyTask, HttpServletResponse response) {try {//加入模板的数据Map<String, Object> data = new HashMap<>();data.put("key", value);String outFileName = "测试.docx";//模板相对路径String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();path = path + "templates/";//linuxif ("/".equals(File.separator)) {path = path.replaceAll("\\\\", "/");}File outFile = createDoc(data, outFileName, path, "模板.ftl");if (null != outFile) {response.reset();response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(outFile.getName(), "UTF-8"));//获取文件输入流InputStream in = new FileInputStream(outFile);byte[] buffer = new byte[1024];OutputStream out = response.getOutputStream();int len = 0;while ((len = in.read(buffer)) > 0) {//将缓冲区的数据输出到客户端浏览器out.write(buffer, 0, len);}in.close();out.close();}} catch (Exception e) {e.printStackTrace();}}public static File createDoc(Map<String, Object> dataMap, String outFileName, String templatePath, String templateFileName) throws IOException {//设置模本装置方法和路径,FreeMarker支持多种模板装载方法。可以重servlet,classpath,数据库装载,Configuration configuration = new Configuration();//设置null转为空串configuration.setClassicCompatible(true);configuration.setDefaultEncoding("UTF-8");configuration.setDirectoryForTemplateLoading(new File(templatePath));Template t = null;try {//test.ftl为要装载的模板t = configuration.getTemplate(templateFileName, "UTF-8");t.setOutputEncoding("UTF-8");} catch (IOException e) {e.printStackTrace();}//输出文档路径及名称File outFile = new File(outFileName);if (!outFile.exists()) {outFile.createNewFile();}Writer out = null;FileOutputStream fos = null;try {fos = new FileOutputStream(outFile);OutputStreamWriter oWriter = new OutputStreamWriter(fos, "UTF-8");//这个地方对流的编码不可或缺,使用main()单独调用时,应该可以,但是如果是web请求导出时导出后word文档就会打不开,并且包XML文件错误。主要是编码格式不正确,无法解析。// out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));out = new BufferedWriter(oWriter);} catch (FileNotFoundException e1) {e1.printStackTrace();}try {t.process(dataMap, out);out.close();fos.close();return outFile;} catch (TemplateException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}
}
我的模板文件放在
下,我是springBoot,前后端分离的maven。获取模板路径视情况而定。
//设置null转为空串
configuration.setClassicCompatible(true);
这个设置下就可以不加感叹号了。然后模板获取数据是根据你起的变量名称和Map的key对应来获取值的,即使模板里面的变量名称加了感叹号,key也不用加感叹号。然后就可以下载了。如果下载后发现有错位现象,多了空格或者换行,比如这种
下载的文件前面肯定多了很多空格或者回车,把多余的删除即可
还有是循环,塞数据的map要put成
List<Map<String, String>> list = new ArrayList<>();
Map<String, String> m = new HashMap<>();
m.put(“anzlsz01”,value);
list .add(m);
data.put(“TABLE1”, list);模板要这样