有这样一个需求:需要将用户输入的数据填写到准备好的Word模版中并提供下载,最终选择POI-tl和POI来完成上述需求。
在这个过程中,主要遇到了以下两个问题:
1. Word的两个格式doc和docx(两种文件的区别大家可以自行百度了解下),POI并没有提供统一的处理类。分别用 HWPFDocument 处理doc文档,用 XWPFTemplate 处理docx文档。
2. 在用 HWPFDocument 处理doc文档时,如果遇到一些复杂的表格(像是有合并的单元格时),会导致一读取doc文档,生成的doc文档就无法打开,一般打开时,word会报如下错误:
(这里不知道是为什么,如果有大佬知道原因,希望可以指教下)。而将文件模版转换为docx后,可以正常填充。
所以这里采取的解决办法是:先将文件模版转换为docx,填充好后,再通过POI转换为doc。(其中docx转换为doc的代码看了好多都没有好用的,试了下将在输入输出流进行读取时候,将文件名换一下,居然好用了,然而我并没有搞明白是为什么,同样希望可以请教下大佬。)
下面是填充Word并提供下载的完整代码:
/**
* Word模版填充工具类
*/
public class WordUtil {
/**
* .doc和.docx类型的文件需要分别进行处理,
* 同时由于无法正确读取部分doc文档,采用先进行docx填充,再转换为doc方案
*/
public static void fillAndDownloadWordDocument(String templateFilePath, Map<String, String> dataMap, HttpServletResponse response, String cacheFilePath) {
// 判断文件类型,根据不同类填充
if (templateFilePath.endsWith(".docx") && templateFilePath.contains("复杂表格模板")) {
fillAndConvertDocxToDoc(templateFilePath, dataMap, cacheFilePath, response);
} else if (templateFilePath.endsWith(".docx")) {
fillAndDownloadDocx(templateFilePath, dataMap, response);
} else if (templateFilePath.endsWith(".doc")) {
fillAndDownloadDoc(templateFilePath, dataMap, response);
} else {
throw new RuntimeException("文件类型错误");
}
}
/**
* 将数据填充到对应word模版中,并提供下载docx类型文件
*
* @param templateFilePath 文件名称
* @param dataMap 填充word模板中的数据
*/
private static void fillAndDownloadDocx(String templateFilePath, Map<String, String> dataMap, HttpServletResponse response) {
ConfigureBuilder builder = Configure.newBuilder();
builder.buildGramer("${", "}");
XWPFTemplate template;
try {
template = XWPFTemplate.compile(new FileInputStream(templateFilePath), builder.build()).render(dataMap);
} catch (IOException e) {
throw new RuntimeException("文件模版填充失败");
}
if (template == null) {
throw new RuntimeException("文件模版填充失败");
}
//浏览器单个下载生成的word文件
// 文件名 日期-文件名
StringBuffer resultDocName = new StringBuffer();
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HHmmss");
resultDocName.append(dateFormat.format(date)).append(templateFilePath.substring(templateFilePath.lastIndexOf("\\") + 1));
String exportFileName = resultDocName.toString();
ServletOutputStream outputStream = null;
// 进行文件下载
try {
outputStream = response.getOutputStream();
// 对文件名进行编码处理中文问题
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(exportFileName, "UTF-8"));
response.setCharacterEncoding("UTF-8");
template.write(outputStream);
} catch (Exception e) {
throw new RuntimeException("文件下载失败");
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
template.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 将数据填充到对应word模版中,并提供下载doc类型文件
*
* @param templateFilePath 导入文件路径
* @param dataMap 填充word模板中的数据
*/
private static void fillAndDownloadDoc(String templateFilePath, Map<String, String> dataMap, HttpServletResponse response) {
HWPFDocument doc;
try {
doc = new HWPFDocument(new FileInputStream(templateFilePath));
} catch (IOException e) {
throw new RuntimeException("加载word文件模版失败");
}
// 填充内容
Range range = doc.getRange();
for (Map.Entry<String, String> entry : dataMap.entrySet()) {
// 这里需要判空,poi内部会调用value.length
String value = entry.getValue();
if (value == null) {
value = "";
}
range.replaceText(build(entry.getKey()), value);
}
// 填充页眉
Range headerStoryRange = doc.getHeaderStoryRange();
for (Map.Entry<String, String> entry : dataMap.entrySet()) {
String value = entry.getValue();
if (value == null) {
value = "";
}
headerStoryRange.replaceText(build(entry.getKey()), value);
}
//浏览器单个下载生成的word文件
// 文件名 日期-文件名
StringBuffer resultDocName = new StringBuffer();
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HHmmss");
resultDocName.append(dateFormat.format(date)).append(templateFilePath.substring(templateFilePath.lastIndexOf("\\") + 1));
String exportFileName = resultDocName.toString();
ServletOutputStream outputStream = null;
// 进行文件下载
try {
outputStream = response.getOutputStream();
// 对文件名进行编码处理中文问题
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(exportFileName, "UTF-8"));
response.setCharacterEncoding("UTF-8");
doc.write(outputStream);
} catch (Exception e) {
throw new RuntimeException("文件下载失败");
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
doc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static String build(String s) {
return "${" + s + "}";
}
/**
* 由于部分doc文档,poi无法直接读取,采用以下方案进行
* 1.外界将模版保存为docx,进行填充
* 2.填充后docx保存至本地,转换为doc
* 3.提供下载,删除过程中产生的文件
*
* @param templateFilePath 模版文件位置
* @param dataMap 填充数据
* @param cacheFilePath 文件缓存目录
*/
private static void fillAndConvertDocxToDoc(String templateFilePath, Map<String, String> dataMap, String cacheFilePath, HttpServletResponse response) {
ConfigureBuilder builder = Configure.newBuilder();
builder.buildGramer("${", "}");
XWPFTemplate template;
try {
template = XWPFTemplate.compile(new FileInputStream(templateFilePath), builder.build()).render(dataMap);
} catch (IOException e) {
throw new RuntimeException("文件模版填充失败");
}
if (template == null) {
throw new RuntimeException("文件模版填充失败");
}
//浏览器单个下载生成的word文件
// 文件名 日期-文件名
StringBuffer resultDocName = new StringBuffer();
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HHmmss");
resultDocName.append(dateFormat.format(date)).append(templateFilePath.substring(templateFilePath.lastIndexOf("\\") + 1));
String exportFileName = resultDocName.toString();
String exportFilePath = cacheFilePath + exportFileName;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(exportFilePath);
template.write(fos);
} catch (IOException e) {
System.out.println(exportFileName + "文件保存失败");
} finally {
try {
if (fos != null) {
fos.close();
}
template.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 填充完毕后,进行docx转doc,并提供下载
String docName = exportFileName.replaceFirst(".docx", ".doc");
String docPath = cacheFilePath + docName;
convertDocxToDoc(exportFilePath, docPath);
downloadWord(response, docName, docPath);
deleteFile(exportFilePath);
deleteFile(docPath);
}
/**
* 上传文件
*
* @param response 响应
* @param filePath 文件路径
*/
private static void downloadWord(HttpServletResponse response, String exportFileName, String filePath) {
FileInputStream fis = null;
ServletOutputStream outputStream = null;
try {
fis = new FileInputStream(filePath);
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(exportFileName, "UTF-8"));
response.setCharacterEncoding("UTF-8");
outputStream = response.getOutputStream();
byte[] bytes = new byte[1024];
int readCount;
while ((readCount = fis.read(bytes)) != -1) {
outputStream.write(bytes, 0, readCount);
}
fis.close();
outputStream.close();
} catch (IOException e) {
System.out.println(filePath + "文件上传失败");
} finally {
try {
if (fis != null) {
fis.close();
}
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* docx文档转换为doc文档
*
* @param docxPath docx路径
* @param docPath doc路径
*/
private static void convertDocxToDoc(String docxPath, String docPath) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(docxPath);
XWPFDocument docx = new XWPFDocument(fis);
fos = new FileOutputStream(docPath);
docx.write(fos);
} catch (IOException e) {
System.out.println(docxPath + "文件转换失败");
} finally {
try {
if (fis != null) {
fis.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 删除文件
*
* @param filePath 文件路径
*/
private static void deleteFile(String filePath) {
File file = new File(filePath);
if (file.exists() && file.isFile()) {
try {
boolean delete = file.delete();
if (!delete) {
System.out.println("文件删除失败");
}
} catch (Exception e) {
System.out.println("文件删除失败");
}
}
}
}
我这里用的依赖是比较老的依赖了,其他版本api名字可能有不同,当然也要注意下不同版本的poi的依赖之间可能会有冲突。所用依赖如下:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.0.0</version>
</dependency>
有错误还请各位兄弟指出
标签:docx,Word,String,templateFilePath,POI,文档,doc,new,response From: https://blog.csdn.net/m0_63400588/article/details/137440297