java实现导入word模板导入试题
最近有一个项目需要将一个word文档中的试题数据导入,并存储到数据库中。试题类型包括:单选题、多选题、判断题、填空题、简答题。支持图片导入(我的这篇是借鉴JAVA实现Excel、Word模板导入 - JAVA - 华仔部落,java poi 解析上传word试卷(题库管理系统) - 爱码网)这两位大神的。
废话不多说,进入正题,直接上代码。
一、word模板

编辑
要求:
1)、题目间隔:上一题与下一题之间必须换行,开始下一题;
2)、试题编辑的样式:[单选题][答案][章][节][考点][难度] [解析]是固定样式,不能删除。 如果试题无[解析],无[章][节][考点][难度],可以不填。
二、代码:
1、引入依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.1</version>
</dependency>
2、创建VO对象TestQuestionsImportVo和TestQuestionsImportWordVo
/**
* 试题导入对应字段
*/
@Data
public class TestQuestionsImportVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 试题类型1=单选2=多选3=判断4=填空5=简答,6=不定项,7=材料题
*/
private Integer type;
/**
* 题目名称
*/
private String name;
/**
* 答案
*/
private String answer;
/**
* 选项A
*/
private String optionA;
/**
* 选项B
*/
private String optionB;
/**
* 选项C
*/
private String optionC;
/**
* 选项D
*/
private String optionD;
/**
* 选项E
*/
private String optionE;
/**
* 选项F
*/
private String optionF;
/**
* 选项G
*/
private String optionG;
/**
* 选项H
*/
private String optionH;
/**
* 选项I
*/
private String optionI;
/**
* 选项J
*/
private String optionJ;
/**
* 选项K
*/
private String optionK;
/**
* 选项L
*/
private String optionL;
/**
* 文字解析
*/
private String analysis;
/**
* 难度,1=简单,2=一般,3=困难
*/
private String difficulty;
/**
* 题库章名称
*/
private String qbChapName;
/**
* 题库节名称
*/
private String qbNodeName;
/**
* 题库考点名称
*/
private String qbExsiName;
}
/**
* 试题导入对应字段
*/
@Data
public class TestQuestionsImportWordVo extends TestQuestionsImportVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 材料题字试题
*/
private List<TestQuestionsImportWordVo> children;
/**
* 题目名称图片
*/
private List<XWPFPicture> questionNameXwpfPicture;
/**
* 选项A图片
*/
private List<XWPFPicture> optionXwpfPictureA;
/**
* 选项B图片
*/
private List<XWPFPicture> optionXwpfPictureB;
/**
* 选项C图片
*/
private List<XWPFPicture> optionXwpfPictureC;
/**
* 选项D图片
*/
private List<XWPFPicture> optionXwpfPictureD;
/**
* 选项E图片
*/
private List<XWPFPicture> optionXwpfPictureE;
/**
* 选项F图片
*/
private List<XWPFPicture> optionXwpfPictureF;
/**
* 选项G图片
*/
private List<XWPFPicture> optionXwpfPictureG;
/**
* 选项H图片
*/
private List<XWPFPicture> optionXwpfPictureH;
/**
* 选项I图片
*/
private List<XWPFPicture> optionXwpfPictureI;
/**
* 选项J图片
*/
private List<XWPFPicture> optionXwpfPictureJ;
/**
* 选项K图片
*/
private List<XWPFPicture> optionXwpfPictureK;
/**
* 选项L图片
*/
private List<XWPFPicture> optionXwpfPictureL;
}
3、word解析试题核心代码
/**
* word导入处理
*/
public class ImportWordParagraphHandleUtil {
// 该正则用来匹配一个大题(例如:二、多选题)
private static String bigQuestionRegex = "([一|二|三|四|五|六|七|八|九|十]{1,3})([、.]{1})([\\u4E00-\\u9FA5\\s]+题)";
// 该正则用来匹配一个选项(例如:A\B\C\D)
private static String optionRegex = "([A|B|C|D|E|F|G|H|I|J|K|L]{1,3})";
/**
* 解析导入题目word模板
*
* @param file
*/
public static List<TestQuestionsImportWordVo> analysisImportQuestionSubjectWord(MultipartFile file) throws IOException {
List<TestQuestionsImportWordVo> questionsImportWordVoList = new ArrayList<>();
// 获取文件流
InputStream fileInputStream = file.getInputStream();
// 解析文档
XWPFDocument xd = new XWPFDocument(fileInputStream);
// 获取全部的文本段落
List<XWPFParagraph> xwPfParagraphList = xd.getParagraphs();
// 过滤掉 大题(例如:二、多选题) 开头的段落
xwPfParagraphList = xwPfParagraphList.stream().filter(xwpfParagraph -> !Pattern.compile(bigQuestionRegex).matcher(getObjectValue(xwpfParagraph.getText())).find()).collect(Collectors.toList());
// 题目段落列表
List<List<XWPFParagraph>> subjectParagraphList = generateSubjectParagraphList(xwPfParagraphList);
if (CollectionUtil.isEmpty(subjectParagraphList)) {
return questionsImportWordVoList;
}
for (List<XWPFParagraph> xwpfParagraphs : subjectParagraphList) {
// 解析考试题目保存对象根据段落列表
TestQuestionsImportWordVo testQuestionsImportWordVo = analysisQuestionsSubjectByParagraph(xwpfParagraphs);
questionsImportWordVoList.add(testQuestionsImportWordVo);
}
return questionsImportWordVoList;
}
/**
* 按照题目生成 段落列表
* 每个题目一个段落列表
*
* @param xwPfParagraphLis
* @return
*/
private static List<List<XWPFParagraph>> generateSubjectParagraphList(List<XWPFParagraph> xwPfParagraphLis) {
List<List<XWPFParagraph>> list = new ArrayList<>();
if (CollectionUtil.isEmpty(xwPfParagraphLis)) {
return list;
}
boolean isStart = false;
list.add(new ArrayList<XWPFParagraph>());
for (int i = 9; i < xwPfParagraphLis.size(); i++) {
// 当前项
XWPFParagraph xwPfParagraphLi = xwPfParagraphLis.get(i);
// 获取文本内容
String text = xwPfParagraphLi.getText();
//获取图片内容
List<XWPFPicture> pictures = StrUtil.isEmpty(text) ? CollectionUtil.isNotEmpty(xwPfParagraphLi.getRuns()) ? xwPfParagraphLi.getRuns().get(0).getEmbeddedPictures() : null : null;
// 以题目类型开头 算做起始段落
if (MapUtil.isNotEmpty(getQuestionTypeMap(text))) {
isStart = true;
}
// 开始标志 且 当前是空行
if (isStart && StrUtil.isBlank(text) && CollectionUtil.isEmpty(pictures)) {
// 获取最后一个题目段落列表
List<XWPFParagraph> last = list.get(list.size() - 1);
// 段落列表不为空
if (last.size() > 0) {
// 算做结束段落
isStart = false;
// 空行不是最后一行时
if (i < xwPfParagraphLis.size() - 1) {
// 继续添加段落列表
list.add(new ArrayList<XWPFParagraph>());
}
}
}
// 处于开始标志时
if (isStart) {
// 获取最后一个题目段落列表
List<XWPFParagraph> last = list.get(list.size() - 1);
// 为空处理
last = CollectionUtil.isEmpty(last) ? new ArrayList<>() : last;
// 将当前段落添加到列表中
last.add(xwPfParagraphLi);
list.set(list.size() - 1, last);
}
}
if (CollectionUtil.isNotEmpty(list)) {
// 如果最后一项是空的,就删除最后一项
List<XWPFParagraph> tempList = list.get(list.size() - 1);
if (tempList.size() == 0) {
list.remove(list.size() - 1);
}
}
return list;
}
/**
* 解析考试题目保存对象根据段落列表
*
* @param xwpfParagraphs
* @return
*/
private static TestQuestionsImportWordVo analysisQuestionsSubjectByParagraph(List<XWPFParagraph> xwpfParagraphs) {
// 创建题目保存对象
TestQuestionsImportWordVo testQuestionsImportWordVo = new TestQuestionsImportWordVo();
// 调用方法获取解析对象
List<TestQuestionsImportWordVo> questionsImportWordVoList = generateQuestionSubjectSaveDTOList(xwpfParagraphs);
if (CollectionUtil.isNotEmpty(questionsImportWordVoList)) {
testQuestionsImportWordVo = questionsImportWordVoList.get(0);
}
return testQuestionsImportWordVo;
}
/**
* 生成题目保存对象列表
*
* @return
*/
@SneakyThrows
private static List<TestQuestionsImportWordVo> generateQuestionSubjectSaveDTOList(List<XWPFParagraph> xwpfParagraphs) {
List<TestQuestionsImportWordVo> questionsImportWordVoList = new ArrayList<>();
if (CollectionUtil.isEmpty(xwpfParagraphs)) {
return questionsImportWordVoList;
}
boolean typeFlag = false;
boolean contentFlag = false;
boolean optionsFlag = false;
for (int i = 0; i < xwpfParagraphs.size(); i++) {
XWPFParagraph xwpfParagraph = xwpfParagraphs.get(i);
if (ObjectUtil.isEmpty(xwpfParagraph)) {
continue;
}
// 读取当前段落内容
String text = getObjectValue(xwpfParagraph.getText()).trim();
//试题类型1=单选2=多选3=判断4=填空5=简答
Integer questionType = null;
//题目名称
String questionName = null;
//题目名称图片
List<XWPFPicture> questionNameXwpfPicture = null;
//试题选项
String questionOption = null;
//试题答案
String questionAnswer = null;
//试题章
String questionChapter = null;
//试题节
String questionNode = null;
//考点
String questionExam = null;
//难度
String questionDifficulty = null;
//解析
String questionAnalysis = null;
//保存的试题类
TestQuestionsImportWordVo questionsImportWordVo = null;
if (MapUtil.isNotEmpty(getQuestionTypeMap(text))) {
questionsImportWordVo = new TestQuestionsImportWordVo();
//处理试题类型
questionType = Integer.valueOf(getQuestionTypeMap(text).get("questionType").toString());
questionName = getQuestionTypeMap(text).get("content").toString();
List<XWPFPicture> xwpfPictureList = getXwpfPictureList(xwpfParagraph);
questionNameXwpfPicture = CollectionUtil.isNotEmpty(xwpfPictureList) ? xwpfPictureList : new ArrayList<>();
i++;
//如果标签不是以[答案]或选项A.\A、结尾,该题目为多行一题
if (questionType.intValue() == 5) {
while (!(xwpfParagraphs.get(i).getText().contains(ImportWordQuestionTypeEnum.ANSWER.getDesc()))) {
questionName += getObjectValue(xwpfParagraphs.get(i).getText());
if (CollectionUtil.isNotEmpty(getXwpfPictureList(xwpfParagraphs.get(i)))) {
questionNameXwpfPicture.addAll(getXwpfPictureList(xwpfParagraphs.get(i)));
}
i++;
}
} else {
while (!(xwpfParagraphs.get(i).getText().contains("A"))) {
questionName += getObjectValue(xwpfParagraphs.get(i).getText());
if (CollectionUtil.isNotEmpty(getXwpfPictureList(xwpfParagraphs.get(i)))) {
questionNameXwpfPicture.addAll(getXwpfPictureList(xwpfParagraphs.get(i)));
}
i++;
}
}
typeFlag = true;
contentFlag = true;
}
// 获取选项部分
questionOption = getObjectValue(xwpfParagraphs.get(i).getText());
if (typeFlag && contentFlag && !optionsFlag && questionOption.contains(