问题描述
遇到的问题,使用FreeMarker导出Word后本地打开正常,但上传后在浏览器预览显示的是一个xml。
问题原因
使用FreeMarker模板导出的Word实际上是一个xml文件,Word和WPS支持这种格式,但是网页预览不支持。可以通过修改导出后的.doc文件的后缀验证,修改为.xml后导出的文件可以打开,但本地创建的.doc文件不能打开。
解决方法
将xml文件转换成真正的word文件。
docx4j是一个用于创建和操作Microsoft Open XML (Word docx, PowerPoint pptx, 和 Excel xlsx)文件的Java类库。
引入依赖
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
<version>8.3.3</version>
</dependency>
转换工具类代码
public class FreeMarkerGenWordUtil {
/**
* @description 根据模版+数据生成Word文档 字节流
* @author luxinting email:luxinting@caseeder.com
* @date 2023/10/19 10:23
* @param[1] dataMap 模版动态数据
* @param[2] template 模版
* @return ByteArrayInputStream
* @throws
*/
public static ByteArrayInputStream createWord(Map<String, Object> dataMap, Template template) {
ByteArrayOutputStream byteArrayOutputStream = null;
Writer writer = null;
ByteArrayInputStream inputStream = null;
ByteArrayOutputStream osAfterConvert = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
writer = new OutputStreamWriter(byteArrayOutputStream, "utf-8");
template.process(dataMap, writer);
inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
//兼容微软Office Word
WordprocessingMLPackage word = Docx4J.load(inputStream);
osAfterConvert = new ByteArrayOutputStream();
Docx4J.save(word, osAfterConvert);
return new ByteArrayInputStream(osAfterConvert.toByteArray());
} catch (IOException | TemplateException | Docx4JException e) {
e.printStackTrace();
return null;
} finally {
if (byteArrayOutputStream != null) {
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (osAfterConvert != null) {
try {
osAfterConvert.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
将生成的.docx文件导出的方法
public Result exportForceNoticeWordFile(HttpServletResponse response, Long reportId) {
if (reportId == null) {
return DataResultUtil.fail("报告ID不能为空");
}
//获取数据
Map<String, Object> dataMap = buildExportDataMap(reportId);
if (dataMap == null) {
return DataResultUtil.fail("报告ID不正确");
}
Configuration configuration = new Configuration(Configuration.VERSION_2_3_31);
ServletOutputStream sos = null;
ByteArrayInputStream inputStream = null;
try {
// 加载文档模板FTL文件所存在的位置
configuration.setTemplateLoader(new ClassTemplateLoader(this.getClass(), "/templates/"));
configuration.setDefaultEncoding("UTF-8");
Template template = configuration.getTemplate("RecallDeviceForce.ftl");
inputStream = FreeMarkerGenWordUtil.createWord(dataMap, template);
if (inputStream==null) {
return DataResultUtil.fail("导出失败,导出模板处理异常");
}
response.setContentType("multipart/form-data");
//为文件重新设置名字
String fileName = "通知书.docx";
response.addHeader("Content-Disposition", "attachment; filename=\"" + new String((fileName).getBytes("UTF-8"), "ISO8859-1") + "\"");
sos = response.getOutputStream();
//读取文件流
int len = 0;
byte[] buffer = new byte[1024 * 10];
while ((len = inputStream.read(buffer)) != -1) {
sos.write(buffer, 0, len);
}
sos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
LogUtil.error("导出异常:" + e.getMessage());
e.printStackTrace();
}
}
if (sos != null) {
try {
sos.close();
} catch (IOException e) {
LogUtil.error("导出异常:" + e.getMessage());
e.printStackTrace();
}
}
}
return DataResultUtil.success();
}