上一篇文章当中记录了如何使用POI根据模板生成word文档,但是在生成时只能替换已经设置好的关键字,以及生成表格。但是在实际编码过程中有一个是每个方案都要在一个页面上显示的需求,所以本文主要是对上一篇的改版使其可以实现不同方案之间可以分页的问题。
主要参考连接:
https://blog.csdn.net/qq_38638512/article/details/80254267
https://www.cnblogs.com/ssbfs/p/11251564.html
直接上代码:
/**
* 根据模板生成word文档
* @param projectName 方案名称
* @param userCount 人事
* @param roleList 角色列表
* @param groupList 小组列表(分几页,每页显示的内容)
* @param templatePath 模板地址
* @param dir 服务器目录
* @return
*/
public String exportWord(String projectName,Integer userCount,List<Map<String,String>> roleList,List<ExamProjectGroup> groupList,String templatePath,String dir){
String filePath = "";
Map<String,Object> dataMap = new HashMap<String, Object>();
try {
List<Map<String,String>> mapList = new ArrayList<>();
Map contentMap = null;
Map roleNameMap = null;
List<Integer> groupIds = new ArrayList<>();
//第一次循环,循环出所有小组,每个小组生成一页
for (int i = 0; i < groupList.size(); i++) {
contentMap = new HashMap();
//第二层循环,查出角色以及姓名(注意当前已经按照角色排序)
for (int j = 0; j < 10; j++) {
if(roleList.size() > j) {
roleNameMap = roleList.get(j);
contentMap.put("roleName" + (j + 1), roleNameMap.get("roleName"));
contentMap.put("userName" + (j + 1), roleNameMap.get("userName"));
}else {
contentMap.put("roleName" + (j + 1), "");
contentMap.put("userName" + (j + 1), "");
}
}
contentMap.put("groupName", groupList.get(i).getGroupName());
contentMap.put("number",userCount+"" );
contentMap.put("title",projectName );
//最后一页不在分页
// if(i != groupList.size()-1){
// contentMap.put("line", "<w:p><w:r><w:br w:type=\"page\"/></w:r></w:p>");
// }else {
// contentMap.put("line","");
// }
mapList.add(contentMap);
}
dataMap.put("columns",mapList);
filePath = exportWordUtils(mapList,templatePath,dir);
} catch (Exception e) {
e.printStackTrace();
}
return filePath;
}
/**
* 生成word文档
* @param mapList 普通占位符
* @param templatePath 模板地址
* @param fileDir 服务器目录
* @return
* @throws Exception
*/
public String exportWordUtils( List<Map<String, String>> mapList,String templatePath,String fileDir) throws Exception{
//我用的是2007版本以后poi对word进行解析的docx
//首先定义一个XWPFDocument 集合 这个对象可以进行word 解析 合并 还有下载都离不开这个对象
List<XWPFDocument> xwpfDocuments = new ArrayList<XWPFDocument>();
//期次我们先解析word模板 替换其中的占位符
for (Map<String, String> page : mapList) {
//返回一个新的xwpfDocument对象
XWPFDocument xwpfDocument = generateWord(page,templatePath);
xwpfDocuments.add(xwpfDocument);
}
//这样第一步将所有word内容替换之后生成多个 xwpfDocument
//现在将多个xwpfDocument 进行合并 追加 生成word文件
//先获取一个模板 以第一个模板为基础进行追加
XWPFDocument xwpfDocument = xwpfDocuments.get(0);
for (int i = 0; i < xwpfDocuments.size(); i++) {
//每次的追加为了避免样式和格式混乱 加上分页符
//当是只有一条数据的时候 直接输出
if (i==0) {
xwpfDocument=xwpfDocuments.get(0);
continue;
}else {
//当存在多条时候
xwpfDocument=mergeWord(xwpfDocument,xwpfDocuments.get(i));
}
}
//合并之后返回XWPFDocument对象 写出就可以了
String wordPath = System.currentTimeMillis()+"projectFile.docx";
String wordAllPath = fileDir+File.separator+wordPath;
FileOutputStream fout = new FileOutputStream(wordAllPath);
xwpfDocument.write(fout);
return wordPath;
}
/**
* 替换word占位符的内容 避免了不解析的情况出现
* @param param
* @param filePath
* @return
*/
public static XWPFDocument generateWord(Map<String, String> param, String filePath) {
XWPFDocument doc = null;
try {
OPCPackage pack = POIXMLDocument.openPackage(filePath);
doc = new XWPFDocument(pack);
if (param != null && param.size() > 0) {
//处理段落
String tempString = "";
Set<XWPFRun> runSet = new HashSet<>();
char lastChar = ' ';
List<XWPFParagraph> paragraphList = doc.getParagraphs();
if(paragraphList != null && paragraphList.size() > 0){
for(XWPFParagraph paragraph:paragraphList){
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if(text==null)continue;
text = replaceText(text,param);
run.setText("",0);
run.setText(text,0);
for(int i=0;i<text.length();i++){
char ch = text.charAt(i);
if(ch == '$'){
runSet = new HashSet<>();
runSet.add(run);
tempString = text;
}else if(ch == '{'){
if(lastChar == '$'){
if(runSet.contains(run)){
}else{
runSet.add(run);
tempString = tempString+text;
}
}else{
runSet = new HashSet<>();
tempString = "";
}
}else if(ch == '}'){
if(tempString!=null&&tempString.indexOf("${")>=0){
if(runSet.contains(run)){
}else{
runSet.add(run);
tempString = tempString+text;
}
}else{
runSet = new HashSet<>();
tempString = "";
}
if(runSet.size()>0){
String replaceText = replaceText(tempString,param);
if(!replaceText.equals(tempString)){
int index = 0;
XWPFRun aRun = null;
for(XWPFRun tempRun:runSet){
tempRun.setText("",0);
if(index==0){
aRun = tempRun;
}
index++;
}
aRun.setText(replaceText,0);
}
runSet = new HashSet<>();
tempString = "";
}
}else{
if(runSet.size()<=0)continue;
if(runSet.contains(run))continue;
runSet.add(run);
tempString = tempString+text;
}
lastChar = ch;
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return doc;
}
private static String replaceText(String text, Map<String, String> map) {
if(text != null){
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
if(text.indexOf(key) != -1){
Object value = entry.getValue();
if (value instanceof String) {//文本替换
text = text.replace(key, value.toString());
}
}
}
}
return text;
}
//两个对象进行追加
public XWPFDocument mergeWord(XWPFDocument document,XWPFDocument doucDocument2) throws Exception {
XWPFDocument src1Document =document ;
XWPFParagraph p = src1Document.createParagraph();
//设置分页符
p.setPageBreak(true);
CTBody src1Body = src1Document.getDocument().getBody();
XWPFDocument src2Document = doucDocument2;
CTBody src2Body = src2Document.getDocument().getBody();
XWPFParagraph p2 = src2Document.createParagraph();
XmlOptions optionsOuter = new XmlOptions();
optionsOuter.setSaveOuter();
String appendString = src2Body.xmlText(optionsOuter);
String srcString = src1Body.xmlText();
String prefix = srcString.substring(0,srcString.indexOf(">")+1);
String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
String sufix = srcString.substring( srcString.lastIndexOf("<") );
String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
CTBody makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix);
src1Body.set(makeBody);
return src1Document;
}
测试使用:
@Test
public void template2Word(){
//服务器项目路径
//String serverPath = request.getSession().getServletContext().getRealPath("/");
String serverPath = "";
//模板地址
String templatePath = "/Users/xuwang/Desktop/projectTemplate.docx";
//获取所有小组
List<ExamProjectGroup> examProjectGroups = examProjectGroupService.getAll();
//获取方案中所有的人员信息
List examProjectGroupUserDTOList = examProjectGroupUserService.getAll();
List<Map<String,String>> mapList = new ArrayList<>();
Map map = null;
List<String> roleName = new ArrayList<>();
int count = -1;
for (int i = 0; i < examProjectGroupUserDTOList.size(); i++) {
if(roleName.contains(examProjectGroupUserDTOList.get(i).getRoleName())){
mapList.get(count);
if(map.get("groupId").equals(examProjectGroupUserDTOList.get(i).getGroupId())) {
map.put("userName", map.get("userName")+" " + examProjectGroupUserDTOList.get(i).getUserName());
}
}else {
map = new HashMap();
roleName.add(examProjectGroupUserDTOList.get(i).getRoleName());
map.put("roleName",examProjectGroupUserDTOList.get(i).getRoleName());
map.put("userName",":"+examProjectGroupUserDTOList.get(i).getUserName());
map.put("groupId",examProjectGroupUserDTOList.get(i).getGroupId());
mapList.add(map);
count ++;
}
}
String filePath = exportWord("方案名称",examProjectGroupUserDTOList.size(),mapList,examProjectGroups,templatePath,serverPath+ File.separator+"upload" +File.separator+"docs");
//
}
模板:
最终效果:
{{ cmt.username }}
{{ cmt.content }}
{{ cmt.commentDate | formatDate('YYYY.MM.DD hh:mm') }}