最近项目要导出word报告,网上找了2天,总结了两种方式,因为习惯性的ctrl c +ctrl f,原文链接现在找不到了,请谅解。
因为我需要对模块进行设计排版,综合考虑用xml来做。
“用XML做就很简单了。Word从2003开始支持XML格式,大致的思路是先用office2003或者2007编辑好word的样式,然后另存为xml,将xml翻译为FreeMarker模板,最后用java来解析FreeMarker模板并输出Doc。经测试这样方式生成的word文档完全符合office标准,样式、内容控制非常便利,打印也不会变形,生成的文档和office中编辑文档完全一样。”
摘自《java导出生成word》
https://www.cnblogs.com/lcngu/p/5247179.html
以下是结合我自己的项目编写的内容,核心逻辑代码可以直接使用,希望帮助到各位。
一.添加依赖
pom.xml文件增加以下依赖:
<!--word依赖--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.1.6</version> </dependency>
二.实现方式
(一)不调用数据库,适合简单word样式:
结构 :WordController+WordService+WordServiceImpl+WordConfig+UserList(ftl)
代码:(import类和包我就不放了,idea提示导入吧)
WordController:
@Controller @RequestMapping(value = "/word") public class WordController { final static String TEST_WORD_FTL = "templates/*"; //设置word模板路径,一般存放在resources下的templates文件夹,如 templates/word.ftl
@Autowired WordService wordService; //浏览器导出word(无对应mapper) @RequestMapping(value = "/createUserListWord", method = RequestMethod.GET) public ResponseEntity<Void> createUserListWord() { wordService.createUserListWord(); return ResponseEntity.ok().build(); } }
WordService:
public interface WordService { void createUserListWord(); }
WordServiceImpl:
@Service public class WordServiceImpl implements WordService { public void createUserListWord() { Map<?, ?> root = initData(); //数据源对象 String template = "/template/test.ftl"; //模板文件的地址 String path = "D:\\UserList.doc"; //生成的word文档的输出地址 WordConfig.process(root, template, path); } private Map<?, ?> initData() { //数据处理 Map<String, Object> root = new HashMap<String, Object>(); List<User> users = new ArrayList<User>(); User zhangsan = new User("张三", "123"); User lisi = new User("李四", "456"); User wangwu = new User("王五", "789"); users.add(zhangsan); users.add(lisi); users.add(wangwu); root.put("users", users); root.put("title", "用户列表"); return root; } }
WordConfig:(freemarker核心实现)
/** * FreeMarker中生成word文档的核心函数 * * @author WZC on 2021/8/9 */ public final class WordConfig { private static Configuration configuration = null; private WordConfig() { throw new AssertionError(); } /** * 根据模板生成相应的文件 * @param root 保存数据的map * @param template 模板文件的地址 * @param path 生成的word文档输出地址 * @return */ public static synchronized File process(Map<?, ?> root, String template, String path) { if (null == root ) { throw new RuntimeException("数据不能为空"); } if (null == template) { throw new RuntimeException("模板文件不能为空"); } if (null == path) { throw new RuntimeException("输出路径不能为空"); } File file = new File(path); String templatePath = template.substring(0, template.lastIndexOf("/")); String templateName = template.substring(template.lastIndexOf("/") + 1, template.length()); if (null == configuration) { configuration = new Configuration(Configuration.VERSION_2_3_23); // 这里Configurantion对象不能有两个,否则多线程访问会报错 configuration.setDefaultEncoding("utf-8"); configuration.setClassicCompatible(true); } configuration.setClassForTemplateLoading(WordConfig.class, templatePath); Template t = null; try { t = configuration.getTemplate(templateName); Writer w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8")); t.process(root, w); // 这里w是一个输出地址,可以输出到任何位置,如控制台,网页等 w.close(); } catch (Exception e) { throw new RuntimeException(e); } return file; } }
User:(实体类)
@Setter @Getter @AllArgsConstructor public class User { private String username; private String password;
}
UserList.ftl:
讲一下过程,首先,创建空的word文档,编写模板:
然后将word另存为xml文件,保存退出用Notepad++或其他xml编辑器打开,检查代码,名称是否正确,有时会出现user.name 被分开为 u ser.name两段,只保留一个<w:t></w:t>标签。
找到<w:t>${user.username}</w:t>
往上找到<w:tr>标签,在上面增加
<#list users as user> 需要遍历输出时添加,如果是固定格式也可以依次${}.
users是list的名字,user对应实体类名称。
</w:tr>后别忘了加上</#list>
检查无误后,将xml文件改为ftl文件,UserList.ftl
复制文件到teample文件夹下,
运行项目,打开浏览器输入http://localhost:15003/inspectManagement/word/createUserListWord
找到生成word目录D:\\UserList.doc,打开
以上为第一种简单的遍历方式 。