记录
最近接到一个需求,将信息按照指定模板导出成word文档。基于SpringBoot,实现如下。
引入依赖
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>3.0.3</version>
</dependency>
准备word模板
准备好模板,将模板放在resource的static目录下
编写业务逻辑并导出到模板
此处参考了大佬的笔记java如何导出word和wps文档,但在发布运行的时候出现了问题,问题出现以下代码中。
String path = "";
try {
//取到模板所在系统中的静态资源位置
path = ResourceUtils.getFile("classpath:static/wps/handleTip.docx").getPath();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
ExportWord.exportWord(path,"test", "HandleTip.docx", hashMap,request,response);
在idea本地环境启动时,程序可以通过模板路径拿到模板资源。但在jar包运行的过程中,路径会发生变化,此时程序根据路径找不到对应的模板,就会发生NPE。此时需要将模板通过输入流的形式进行传参,而不是资源路径
InputStream is = null;
try {
//取到模板所在系统中的静态资源的输入流
ClassPathResource resource = new ClassPathResource("static/test.docx");
is = resource.getInputStream();
} catch (Exception e) {
e.printStackTrace();
}
同时对应的工具类也需要根据传参进行相应的修改。原工具类中通过路径获取到模板对象完成属性设置,此时只需要根据InputStream进行相同操作即可。
原:
XWPFDocument doc = WordExportUtil.exportWord07(templatePath, params);
现:
WordExportUtil.exportWord07(new MyXWPFDocument(is), params);
完整的业务逻辑如下:
public void exportWord(String id, HttpServletRequest request, HttpServletResponse response) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Organization model = this.selectById(id);
//处理模板需要的参数信息
Map<String, Object> hashMap = Maps.newHashMap();
//通过反射完成所有参数值的设置
Field[] fields = OrganizationListReflectionDto.class.getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
Object value = OrganizationList.class.getDeclaredMethod("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1)).invoke(model);
hashMap.put(fieldName, value);
}
hashMap.put("title", "测试");
InputStream is = null;
try {
//取到模板所在系统中的静态资源的输入流
ClassPathResource resource = new ClassPathResource("static/specialOrg.docx");
is = resource.getInputStream();
} catch (Exception e) {
e.printStackTrace();
}
//导出word
ExportWordUtil.exportWord(is,"test", "test.docx", hashMap, request, response);
}
工具类
public static void exportWord(InputStream is, String temDir, String fileName, Map<String,Object> params, HttpServletRequest request, HttpServletResponse response){
Assert.notNull(temDir,"临时文件路径不能为空");
Assert.notNull(temDir,"导出文件名不能为空");
Assert.isTrue(fileName.endsWith(".docx"),"word导出请使用docx格式");
FileOutputStream fos = null;
OutputStream out = null;
if (!temDir.endsWith("/")){
temDir = temDir+ File.separator;
}
File dir = new File(temDir);
if (!dir.exists()){
dir.mkdirs();
}
try {
String userAgent = request.getHeader("user-agent").toLowerCase();
if (userAgent.contains("msie")||userAgent.contains("like gecko")){
fileName = URLEncoder.encode(fileName,"UTF-8");
}else {
fileName = new String(fileName.getBytes("utf-8"),"ISO-8859-1");
}
WordExportUtil.exportWord07(new MyXWPFDocument(is), params);
String tmpPath = temDir + fileName;
fos = new FileOutputStream(tmpPath);
doc.write(fos);
//设置强制下载不打开
response.setContentType("application/force-download");
//设置文件名
response.addHeader("Content-Disposition","attachment;fileName=" + fileName);
out = response.getOutputStream();
doc.write(out);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
assert out != null;
out.close();
fos.close();
delFileWord(temDir,fileName);
} catch (IOException e) {
e.printStackTrace();
}
}
}