实现方法:
- 解析docx文件并填充word/document.xml文件(不兼容word2003)
- 使用freemarker填充 doc(doc另存为xml格式的文件)(手机端打不开)
- 使用poi方式构建文档(极其繁琐,不灵活)
方法2
docx压缩包格式:
目录结构定义自行搜索
word目录
目录结构:
- inspectReport.docx提供基本的压缩包格式,文档内容为编辑后的模板(为减少大小可清空word/document.xml的内容,media等资源文件保留)
- inspectReport.ftl为docx压缩包中的word/document.xml文件,此文件为文档内容
代码实现:
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class FileUtil {
private static Configuration FM_CONFIG;
private static final Logger LOG = LoggerFactory.getLogger(FileUtil.class);
static {
FM_CONFIG = new Configuration(Configuration.VERSION_2_3_20);
FM_CONFIG.setDefaultEncoding("utf-8");
FM_CONFIG.setClassForTemplateLoading(FileUtil.class, "/template/");
}
/**
* 返回docx格式的文档,手机端也能打开
*
* @param templateName xx
* @param dataMap xx
* @param fileName xx
* @param response xx
* @throws IOException xx
* @throws URISyntaxException xx
*/
public static void generateDocNew(String templateName, Object dataMap, String fileName, HttpServletResponse response) throws IOException, URISyntaxException {
// 模板document.xml名和docx名保持一致
Template template = FM_CONFIG.getTemplate(templateName + ".ftl");
URI path = FileUtil.class.getResource("/doc/" + templateName + ".docx").toURI();
response.setContentType("application/octet-stream");
// 编码文件名,防止文件名乱码
response.setHeader("Content-disposition", "attachment; filename="
+ URLEncoder.encode(fileName, "UTF-8"));
BufferedInputStream buffInputStream = null;
OutputStreamWriter writer = null;
// 不关闭ZipOutputStream会在web下载时数据格式出错,即未调用close方法会导致文件数据流末尾的格式数据未写入
try (ZipFile zipFile = new ZipFile(path.getPath());
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(path.getPath())));
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()))
) {
ZipEntry zipEntry;
while ((zipEntry = zis.getNextEntry()) != null) {
// 只需要zipEntry的名称,其他的根据具体的文件自动进行设置,不要直接传入zipEntry防止closeEntry时校验数据失败
zos.putNextEntry(new ZipEntry(zipEntry.toString()));
if ("word/document.xml".equals(zipEntry.getName())) {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
// 防止服务器默认编码造成问题
writer = new OutputStreamWriter(byteOut, StandardCharsets.UTF_8);
template.process(dataMap, writer);
zos.write(byteOut.toByteArray());
writer.close();
} else {
buffInputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry));
byte[] b = new byte[1024];
int len;
while ((len = buffInputStream.read(b)) != -1) {
//读取len长度,防止写入默认初始化值0
zos.write(b, 0, len);
}
buffInputStream.close();
}
zis.closeEntry();
zos.closeEntry();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (buffInputStream != null) {
buffInputStream.close();
}
if (writer != null) {
writer.close();
}
}
}
}
依赖Maven
<!-- Freemarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
因需求简单暂时只实现目前的效果。