java导出占位符word模板
实际项目中,便于维护模板,采用直接 word里面制作占位符 来导出更为直观,而不是 将word做成tpl模板。
使用XWPFDocument (这种解析xlsx或者docx)和 HWPFDocument(这种解析xls或者doc)。 代码如下:
写磁盘代码:
点击查看代码
// 创建Word模板文件:在开始代码编写之前,我们需要准备一个Word模板文件,模板文件中的文本可以是固定的,也可以使用占位符来表示需要动态填充的内容
//读取Word模板文件
// 我们需要使用Apache POI库来读取Word模板文件,并将其加载到内存中进行后续的操作。代码如下
FileInputStream fis = null;
try {
File sPath = new File("D:/opt/jwxt/file/uploadfile/ly-edu-core-svc-thx/testWord.docx");
// File sPath = new File("/template/testword.doc");
fis = new FileInputStream(sPath);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// HWPFDocument 支持doc XWPFDocument支持docx
XWPFDocument document = null;
FileOutputStream fos = null;
try {
document = new XWPFDocument(fis);
// } catch (IOException e) {
// e.printStackTrace();
// }
// 替换模板中的占位符
//在模板文件中,我们可以使用占位符来表示需要动态填充的内容,比如使用${placeholder}来表示占位符。我们需要遍历Word文档的所有段落和表格,找到包含占位符的位置,并将其替换为实际的内容。代码如下
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null && text.contains("${{成果名称}}")) {
text = text.replace("${{成果名称}}", "替换后的内容");
run.setText(text, 0);
}
}
}
// 表格处理方式
List<XWPFTable> tables = document.getTables();
for (XWPFTable table : tables) {
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
for (XWPFParagraph cellParagraph : cellParagraphs) {
List<XWPFRun> runs = cellParagraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null && text.contains("${{完成人姓名1}}")) {
text = text.replace("${{完成人姓名1}}", "替换后的内容");
run.setText(text, 0);
}
if (text != null && text.contains("${{成果名称}}")) {
text = text.replace("${{成果名称}}", "替换后的内容2222");
run.setText(text, 0);
}
}
}
}
}
}
// 导出word文档
File outFile = new File("D:\\opt\\jwxt\\file\\uploadfile\\ly-edu-core-svc-thx\\占位符完成打印.docx");
fos = new FileOutputStream(outFile);
} catch (Exception e) {
log.error( "获取word文档失败,原因是:" + e.getMessage());
} finally {
try {
document.write(fos);
if(fos != null) {
fos.close();
}
// document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
与前端配合弹出流,使用response,代码如下
点击查看代码
/**
* 我们可以实现Java Word模板打印的功能。首先,我们需要引入Apache POI库,并创建一个Word模板文件。然后,我们需要读取模板文件并将其加载到内存中。
* 接下来,我们需要遍历Word文档,找到包含占位符的位置,并将其替换为实际的内容。最后,我们可以将生成的Word文档导出为文件。
* <p>
* 在实际应用中,我们可以根据具体需求扩展这个功能,比如支持更复杂的模板替换、添加图片和表格等。同时,我们也可以使用其他的库来实现类似的功能,比如Docx4j等
*
*
*/
@Autowired
private HttpServletResponse response;
@Autowired
private HttpServletRequest request;
@Override
public void testWordPrint(ReviewTemplateDto reviewTemplateDto) {
// 创建Word模板文件:在开始代码编写之前,我们需要准备一个Word模板文件,模板文件中的文本可以是固定的,也可以使用占位符来表示需要动态填充的内容
//读取Word模板文件
// 我们需要使用Apache POI库来读取Word模板文件,并将其加载到内存中进行后续的操作。代码如下
FileInputStream fis = null;
try {
File sPath = new File("D:/opt/jwxt/file/uploadfile/tt/模板.docx");
// File sPath = new File("/template/testword.doc");
fis = new FileInputStream(sPath);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// HWPFDocument 支持doc XWPFDocument支持docx
XWPFDocument document = null;
FileOutputStream fos = null;
try {
document = new XWPFDocument(fis);
// 替换模板中的占位符
//在模板文件中,我们可以使用占位符来表示需要动态填充的内容,比如使用${placeholder}来表示占位符。我们需要遍历Word文档的所有段落和表格,找到包含占位符的位置,并将其替换为实际的内容。代码如下
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null && text.contains("${{成果名称}}")) {
text = text.replace("${{成果名称}}", "替换后的内容");
run.setText(text, 0);
}
}
}
// 表格处理方式
List<XWPFTable> tables = document.getTables();
for (XWPFTable table : tables) {
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
for (XWPFParagraph cellParagraph : cellParagraphs) {
List<XWPFRun> runs = cellParagraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null && text.contains("${{完成人姓名1}}")) {
text = text.replace("${{完成人姓名1}}", "替换后的内容");
run.setText(text, 0);
}
if (text != null && text.contains("${{成果名称}}")) {
text = text.replace("${{成果名称}}", "替换后的内容2222");
run.setText(text, 0);
}
}
}
}
}
}
// 导出word文档
// File outFile = new File("D:\\opt\\jwxt\\file\\uploadfile\\tt\\tt.docx");// 直接写磁盘
// fos = new FileOutputStream(outFile);
//
//// document.write(fos);//
String filePath = request.getSession().getServletContext().getRealPath("/");
String fileName = "tt.docx";
// response.setHeader("Content-Disposition", "attachment;fileName=" + stringToUnicode(fileName));
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.setContentType("multipart/form-data");
OutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
document.write(outputStream);
} catch (Exception e) {
log.error("获取word文档失败,原因是:" + e.getMessage());
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
点击查看代码
// 导出占位符word
export function exportZwfWord (url,params,id,callback){
exportDataExcel(url,params,{
header:{ },responseType:"blob"
}).then(response=>{
if (response.status==200){
let fileName = response.headers["content-disposition"].split(";")[1].split("filename=")[1];
const aLink = document.createElement("a");
document.body.appendChild(aLink);
aLink.style.display = "none";
const objectUrl = window.URL.createObjectURL(new Blob([response.data], { type: 'application/docx; charset=utf-8' }));
aLink.href = objectUrl;
aLink.download = decodeURI(fileName);
aLink.click();
document.body.removeChild(aLink);
message.success('导出成功')
callback(response.data);
return;
}else{
if (response.message) {
message.error(response.message);
} else {
message.error("没有数据");
}
callback();
}
})
}
遇到问题:
1。Java使用POI技术导出Word文档,报错Package should contain a content type part [M1.13]
解决方案:
因为XWPFDocument只支持docx,所以需要将doc文件另存为 docx. 切记不能直接改后缀,否则不生效的。
2。问题2:response的Content-Disposition写文件名要与前端对应,否则,导出undefined.
效果:
下面增加补充自动增加表格行:
点击查看代码
package com.ly.education.trainingResource.server.service.impl;
import com.ly.education.commons.util.Encodes;
import com.ly.education.trainingResource.api.dto.ReviewTemplateDto;
import com.ly.education.trainingResource.api.vo.ReviewTemplateVo;
import com.ly.education.trainingResource.server.mapper.WordPrintMapper;
import com.ly.education.trainingResource.server.service.WordPrintService;
import com.ly.spring.boot.uuid.UUIDStringGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 项目管理_评审模板 ReviewTemplate
*
* @author xujinhui
*/
@Slf4j
@Service
public class WordPrintServiceImpl implements WordPrintService {
@Autowired
private WordPrintMapper wordPrintMapper;
@Autowired
private UUIDStringGenerator uuIDStringGenerator;
@Autowired
private HttpServletResponse response;
@Autowired
private HttpServletRequest request;
/**
* 字符串转换unicode
*
* @param string
* @return
*/
private static String stringToUnicode(String string) {
StringBuffer unicode = new StringBuffer();
for (int i = 0; i < string.length(); i++) {
// 取出每一个字符
char c = string.charAt(i);
// 转换为unicode
unicode.append("\\u" + Integer.toHexString(c));
}
return unicode.toString();
}
/**
* 我们可以实现Java Word模板打印的功能。首先,我们需要引入Apache POI库,并创建一个Word模板文件。然后,我们需要读取模板文件并将其加载到内存中。
* 接下来,我们需要遍历Word文档,找到包含占位符的位置,并将其替换为实际的内容。最后,我们可以将生成的Word文档导出为文件。
* <p>
* 在实际应用中,我们可以根据具体需求扩展这个功能,比如支持更复杂的模板替换、添加图片和表格等。同时,我们也可以使用其他的库来实现类似的功能,比如Docx4j等
*
* @param reviewTemplateDto 项目管理_评审模板 ReviewTemplate
*/
@Override
public void testWordPrint(ReviewTemplateDto reviewTemplateDto) {
List<Map<String, Object>> list = wordPrintMapper.testWordPrint(reviewTemplateDto);
// 创建Word模板文件:在开始代码编写之前,我们需要准备一个Word模板文件,模板文件中的文本可以是固定的,也可以使用占位符来表示需要动态填充的内容
//读取Word模板文件
// 我们需要使用Apache POI库来读取Word模板文件,并将其加载到内存中进行后续的操作。代码如下
// 设置文件存储路径(全局根路径/相对路径)
// String fileInPath = FilenameUtils
// .normalize(getAttachmentBasePath().concat(File.separator).concat(relativePath));
FileInputStream fis = null;
try {
File sPath = new File("D:/opt/jwxt/file/uploadfile/ly-edu-core-svc-thx/2.四川轻化工大学研究生教学成果奖申报简表(修改) - 副本.docx");
// File sPath = new File("/template/testword.doc");
fis = new FileInputStream(sPath);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// HWPFDocument 支持doc XWPFDocument支持docx
XWPFDocument document = null;
FileOutputStream fos = null;
try {
document = new XWPFDocument(fis);
// 替换模板中的占位符
//在模板文件中,我们可以使用占位符来表示需要动态填充的内容,比如使用${placeholder}来表示占位符。我们需要遍历Word文档的所有段落和表格,找到包含占位符的位置,并将其替换为实际的内容。代码如下
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null && text.contains("${{成果名称}}")) {
text = text.replace("${{成果名称}}", "替换后的内容");
run.setText(text, 0);
}
}
}
// 表格处理方式
List<XWPFTable> tables = document.getTables();
// 添加行
for (int pAddTabR = 0; pAddTabR < tables.size(); pAddTabR++) {
XWPFTable table = tables.get(pAddTabR);
// 计算表格最后一行的列值的占位符,用于 添加新行时,将占位符写进去
List<String> cellZwfNames = new ArrayList<>();// 最后一行row的占位符的 名称
List<XWPFTableCell> cells = table.getRows().get(table.getRows().size() - 1).getTableCells();
for (int cellContInit = 0; cellContInit < cells.size(); cellContInit++) {
XWPFTableCell cell = cells.get(cellContInit);
String cellInitText = cell.getText();
// if (cellContInit == 0) {
if (StringUtils.isNotBlank(cellInitText)) {
cellInitText = cellInitText.replaceAll("\\$\\{\\{", "").replaceAll("0}}", "");
// cellInitText = "${{" + cellInitText + cellContInit + "}}";
// cellInitText = cellInitText.substring(cellInitText.indexOf("${{")+3,cellInitText.lastIndexOf("}}"));
cellZwfNames.add(cellInitText); // 拼成 ${{姓名}} 占位符
}
if (StringUtils.isBlank(cellInitText)) {
cellZwfNames.add("-");
}
}
// 加新行
int addTotalRows = 5;
if (addTotalRows > 0) {
addWordTableRows(table, table.getRows().size() - 1, 5, table.getRows().size(), cellZwfNames);
}
}
// 替换占位符为查询出来的值
for (XWPFTable table : tables) {
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
for (XWPFParagraph cellParagraph : cellParagraphs) {
List<XWPFRun> runs = cellParagraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null && text.contains("${{完成人姓名1}}")) {
text = text.replace("${{完成人姓名1}}", "替换后的内容");
run.setText(text, 0);
}
if (text != null && text.contains("${{成果名称}}")) {
text = text.replace("${{成果名称}}", "替换后的内容2222");
run.setText(text, 0);
}
}
}
}
}
}
// 导出word文档
// File outFile = new File("D:\\opt\\jwxt\\file\\uploadfile\\ly-edu-core-svc-thx\\四川教学成果占位符完成打印.docx");// 直接写磁盘
// fos = new FileOutputStream(outFile);
//
//// document.write(fos);//
String filePath = request.getSession().getServletContext().getRealPath("/");
String fileName = "导出word.docx";
// response.setHeader("Content-Disposition", "attachment;filename=" + stringToUnicode(fileName));
response.setHeader("Content-Disposition", "attachment;filename=" + Encodes.urlEncode(fileName));
response.setContentType("multipart/form-data");
OutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
document.write(outputStream);
} catch (Exception e) {
log.error("获取word文档失败,原因是:" + e.getMessage());
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* des:表末尾添加行(表,要复制样式的行,添加行数,插入的行下标索引)
*
* @param table
* @param source
* @param rows
* @param cellZwfNames 新增的行的 cell内容 占位符也要自增
*/
public void addWordTableRows(XWPFTable table, int source, int rows, int insertRowIndex, List<String> cellZwfNames) {
try {
//获取表格的总行数
int index = table.getNumberOfRows();
//循环添加行和和单元格
for (int i = 1; i <= rows; i++) {
//获取要复制样式的行
XWPFTableRow sourceRow = table.getRow(source);
//添加新行
XWPFTableRow targetRow = table.insertNewTableRow(insertRowIndex++);
//复制行的样式给新行
targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
//获取要复制样式的行的单元格
List<XWPFTableCell> sourceCells = sourceRow.getTableCells();
//循环复制单元格
for (int newCellIndex = 0; newCellIndex < sourceCells.size(); newCellIndex++) {
XWPFTableCell sourceCell = sourceCells.get(newCellIndex);
//添加新列
XWPFTableCell newCell = targetRow.addNewTableCell();
//复制单元格的样式给新单元格
newCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
//设置垂直居中
newCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);//垂直居中
//复制单元格的居中方式给新单元格
CTPPr pPr = sourceCell.getCTTc().getPList().get(0).getPPr();
if (pPr != null && pPr.getJc() != null && pPr.getJc().getVal() != null) {
CTTc cttc = newCell.getCTTc();
CTP ctp = cttc.getPList().get(0);
CTPPr ctppr = ctp.getPPr();
if (ctppr == null) {
ctppr = ctp.addNewPPr();
}
CTJc ctjc = ctppr.getJc();
if (ctjc == null) {
ctjc = ctppr.addNewJc();
}
ctjc.setVal(pPr.getJc().getVal()); //水平居中
}
//得到复制单元格的段落
List<XWPFParagraph> sourceParagraphs = sourceCell.getParagraphs();
if (StringUtils.isEmpty(sourceCell.getText())) {
continue;
}
//拿到第一段
XWPFParagraph sourceParagraph = sourceParagraphs.get(0);
//得到新单元格的段落
List<XWPFParagraph> targetParagraphs = newCell.getParagraphs();
//判断新单元格是否为空
if (StringUtils.isEmpty(newCell.getText())) {
//添加新的段落
XWPFParagraph ph = newCell.addParagraph();
//复制段落样式给新段落
ph.getCTP().setPPr(sourceParagraph.getCTP().getPPr());
String str = sourceCell.getText();
//得到文本对象
XWPFRun run = ph.getRuns().isEmpty() ? ph.createRun() : ph.getRuns().get(0);
run.setText("${{" + cellZwfNames.get(newCellIndex) + i + "}}");
//复制文本样式
run.setFontFamily(sourceParagraph.getRuns().get(0).getFontFamily());
} else {
XWPFParagraph ph = targetParagraphs.get(0);
ph.getCTP().setPPr(sourceParagraph.getCTP().getPPr());
XWPFRun run = ph.getRuns().isEmpty() ? ph.createRun() : ph.getRuns().get(0);
run.setFontFamily(sourceParagraph.getRuns().get(0).getFontFamily());
}
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
posted on 2023-10-16 13:26 HeavenTang 阅读(699) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!