1.读取word中表格的介绍
这个功能 应该是需要导入word时最常用的功能了
可能有些需求 就是要求系统导入word文档 把一些个人或者项目上的资料转存到数据库 以便在系统上查阅 搜索相关信息
在前1篇文章导入纯文本中 我们看到导入时 是可以指定读取的节点类型的 文本的节点类型就是NodeType.PARAGRAPH
现在我们需要导入读取表格数据 那就要用到表格所需的节点类型
NodeType.TABLE -> 表格
NodeType.ROW -> 表格行
NodeType.CELL -> 表格行中的单元格
2. 准备需要读取的表格(纯文本)
3. 发起请求(纯文本)
4. 读取表格数据(纯文本)
按表格索引读取表格中的内容 适用于读取多个表格的数据
当然 如果表格只有1个 而且内容简单 没有子表 也可以直接按NodeType.ROW类型读取表格行 这种本文不再赘述
/**
* 按表格索引读取表格中的内容 <br/>
* 适用于文档中存在多个表格的读取 每个表格读取时单独写逻辑
* @param wordFile
* @return
*/
@SneakyThrows
@PostMapping("readTableTxtByIndex")
public Ret readTableTxtByIndex(MultipartFile wordFile){
try(InputStream inputStream = wordFile.getInputStream()){
Document doc = new Document(inputStream);
//获取所有的表格
NodeCollection tableNodes = doc.getChildNodes(NodeType.TABLE, true);
int tableCount = tableNodes.getCount();
log.info("表格数量:{}", tableCount);
for (int i = 0; i < tableCount; i++) {
Table table = (Table)tableNodes.get(i);
//按表格索引 单独写逻辑进行数据的读取 本示例中就1个表格 因此就放1个逻辑进行解析了
if(0 == i){
//获取表格中的所有行节点
RowCollection rows = table.getRows();
int rowCount = rows.getCount();
log.info("共有表格行:{}",rowCount);
//获取每一行的单元格节点
for (int j = 0; j < rowCount; j++) {
Row row = rows.get(j);
CellCollection cells = row.getCells();
int cellsCount = cells.getCount();
log.info("第{}行有{}个单元格{}",j,cellsCount);
for (int k = 0; k < cellsCount; k++) {
Cell cell = cells.get(k);
String cellText = cell.getText();
log.info("第{}行 第{}个单元格的文本:{}",j,k,cellText);
}
}
}
}
//本方法是为了演示逐行逐个单元格读取的效果 故采用了fori循环 看起来索引比较多
//实际使用时 可以使用iter循环 直接获取集合内对象
}
return Ret.success();
}
控制台输出
2022-11-25 11:33:09.996 INFO ReadWordHandler : 表格数量:1
2022-11-25 11:33:09.997 INFO ReadWordHandler : 共有表格行:14
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第0行有3个单元格{}
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第0行 第0个单元格的文本:姓名
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第0行 第1个单元格的文本:光头强
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第0行 第2个单元格的文本:
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第1行有3个单元格{}
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第1行 第0个单元格的文本:身份证号
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第1行 第1个单元格的文本:123
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第1行 第2个单元格的文本:
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第2行有3个单元格{}
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第2行 第0个单元格的文本:联系电话
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第2行 第1个单元格的文本:666
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第2行 第2个单元格的文本:
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第3行有4个单元格{}
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第3行 第0个单元格的文本:住址
2022-11-25 11:33:09.997 INFO ReadWordHandler : 第3行 第1个单元格的文本:帝国大厦
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第3行 第2个单元格的文本:学历
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第3行 第3个单元格的文本:翰林
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第4行有1个单元格{}
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第4行 第0个单元格的文本:教育经历
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第5行有4个单元格{}
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第5行 第0个单元格的文本:开始时间
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第5行 第1个单元格的文本:结束时间
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第5行 第2个单元格的文本:学校
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第5行 第3个单元格的文本:专业
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第6行有4个单元格{}
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第6行 第0个单元格的文本:2018-09
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第6行 第1个单元格的文本:2021-07
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第6行 第2个单元格的文本:高中
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第6行 第3个单元格的文本:
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第7行有4个单元格{}
2022-11-25 11:33:09.998 INFO ReadWordHandler : 第7行 第0个单元格的文本:2021-09
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第7行 第1个单元格的文本:至今
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第7行 第2个单元格的文本:大学
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第7行 第3个单元格的文本:四库全书
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第8行有4个单元格{}
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第8行 第0个单元格的文本:
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第8行 第1个单元格的文本:
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第8行 第2个单元格的文本:
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第8行 第3个单元格的文本:
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第9行有1个单元格{}
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第9行 第0个单元格的文本:获得证书
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第10行有2个单元格{}
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第10行 第0个单元格的文本:获得时间
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第10行 第1个单元格的文本:证书名称
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第11行有2个单元格{}
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第11行 第0个单元格的文本:2022-11-01
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第11行 第1个单元格的文本:CET-6级
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第12行有2个单元格{}
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第12行 第0个单元格的文本:2022-12-01
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第12行 第1个单元格的文本:PMP
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第13行有2个单元格{}
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第13行 第0个单元格的文本:
2022-11-25 11:33:09.999 INFO ReadWordHandler : 第13行 第1个单元格的文本:
还是因为粘贴问题 不能看到有些行的文字后面分页符 实际上打印出来的数据
这里 我们可以在读取单元格文本时 增加处理方法
/**
* 获取单元格的文本并移除"\f"分页符
* @param cell 单元格对象
* @return
*/
private String getText(Cell cell){
String text = cell.getText();
return StrUtil.isBlank(text) ? "" : text.trim().replace("\f","");
}
/**
* 按表格索引读取表格中的内容 <br/>
* 适用于文档中存在多个表格的读取 每个表格读取时单独写逻辑
* @param wordFile
* @return
*/
@SneakyThrows
@PostMapping("readTableTxtByIndex")
public Ret readTableTxtByIndex(MultipartFile wordFile){
try(InputStream inputStream = wordFile.getInputStream()){
Document doc = new Document(inputStream);
//获取所有的表格
NodeCollection tableNodes = doc.getChildNodes(NodeType.TABLE, true);
int tableCount = tableNodes.getCount();
log.info("表格数量:{}", tableCount);
for (int i = 0; i < tableCount; i++) {
Table table = (Table)tableNodes.get(i);
//按表格索引 单独写逻辑进行数据的读取 本示例中就1个表格 因此就放1个逻辑进行解析了
if(0 == i){
//获取表格中的所有行节点
RowCollection rows = table.getRows();
int rowCount = rows.getCount();
log.info("共有表格行:{}",rowCount);
//获取每一行的单元格节点
for (int j = 0; j < rowCount; j++) {
Row row = rows.get(j);
CellCollection cells = row.getCells();
int cellsCount = cells.getCount();
log.info("第{}行有{}个单元格{}",j,cellsCount);
for (int k = 0; k < cellsCount; k++) {
Cell cell = cells.get(k);
log.info("第{}行 第{}个单元格的文本:{}",j,k,this.getText(cell));
}
}
}
}
//本方法是为了演示逐行逐个单元格读取的效果 故采用了fori循环 看起来索引比较多
//实际使用时 可以使用iter循环 直接获取集合内对象
}
return Ret.success();
}
这样读取的数据 就没有换页符了
观察比对读取结果与表格内容的对应关系
通过观察 我们可以看到 读取结果准确无误
5.准备需要读取的表格(文本+图片)
6.发起请求(文本+图片)
7.读取表格数据(文本+图片)
@SneakyThrows
@PostMapping("readTable")
public Ret readTable(MultipartFile wordFile){
try(InputStream inputStream = wordFile.getInputStream()){
Document doc = new Document(inputStream);
//获取所有的表格
NodeCollection tableNodes = doc.getChildNodes(NodeType.TABLE, true);
int tableCount = tableNodes.getCount();
log.info("表格数量:{}", tableCount);
for (int i = 0; i < tableCount; i++) {
Table table = (Table)tableNodes.get(i);
//按表格索引 单独写逻辑进行数据的读取 本示例中就1个表格 因此就放1个逻辑进行解析了
if(0 == i){
//获取表格中的所有行节点
RowCollection rows = table.getRows();
int rowCount = rows.getCount();
log.info("共有表格行:{}",rowCount);
//获取每一行的单元格节点
for (int j = 0; j < rowCount; j++) {
Row row = rows.get(j);
CellCollection cells = row.getCells();
int cellsCount = cells.getCount();
log.info("第{}行有{}个单元格{}",j,cellsCount);
for (int k = 0; k < cellsCount; k++) {
Cell cell = cells.get(k);
NodeCollection shapeNodes = cell.getChildNodes(NodeType.SHAPE, true);
if(shapeNodes.getCount() < 1){//文本
log.info("第{}行 第{}个单元格的文本:{}",j,k, AsposeTool.getText(cell));
}else{//图片
for (Object childNode : shapeNodes) {
Shape shape = (Shape)childNode;
byte[] imageBytes = shape.getImageData().getImageBytes();
FileUtil.writeBytes(imageBytes,"C:/Users/Administrator/Desktop/aspose/" + RandomUtil.randomString(3) + ".jpg");
}
}
}
}
}
}
//本方法是为了演示逐行逐个单元格读取的效果 故采用了fori循环 看起来索引比较多
//实际使用时 可以使用iter循环 直接获取集合内对象
}
return Ret.success();
}
控制台输出
2022-11-25 16:49:55.732 INFO ReadWordHandler : 表格数量:1
2022-11-25 16:49:55.732 INFO ReadWordHandler : 共有表格行:14
2022-11-25 16:49:55.732 INFO ReadWordHandler : 第0行有3个单元格{}
2022-11-25 16:49:55.732 INFO ReadWordHandler : 第0行 第0个单元格的文本:姓名
2022-11-25 16:49:55.732 INFO ReadWordHandler : 第0行 第1个单元格的文本:光头强
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第1行有3个单元格{}
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第1行 第0个单元格的文本:身份证号
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第1行 第1个单元格的文本:123
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第1行 第2个单元格的文本:
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第2行有3个单元格{}
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第2行 第0个单元格的文本:联系电话
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第2行 第1个单元格的文本:666
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第2行 第2个单元格的文本:
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第3行有4个单元格{}
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第3行 第0个单元格的文本:住址
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第3行 第1个单元格的文本:帝国大厦
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第3行 第2个单元格的文本:学历
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第3行 第3个单元格的文本:翰林
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第4行有1个单元格{}
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第4行 第0个单元格的文本:教育经历
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第5行有4个单元格{}
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第5行 第0个单元格的文本:开始时间
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第5行 第1个单元格的文本:结束时间
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第5行 第2个单元格的文本:学校
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第5行 第3个单元格的文本:专业
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第6行有4个单元格{}
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第6行 第0个单元格的文本:2018-09
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第6行 第1个单元格的文本:2021-07
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第6行 第2个单元格的文本:高中
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第6行 第3个单元格的文本:
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第7行有4个单元格{}
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第7行 第0个单元格的文本:2021-09
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第7行 第1个单元格的文本:至今
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第7行 第2个单元格的文本:大学
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第7行 第3个单元格的文本:四库全书
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第8行有4个单元格{}
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第8行 第0个单元格的文本:
2022-11-25 16:49:55.743 INFO ReadWordHandler : 第8行 第1个单元格的文本:
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第8行 第2个单元格的文本:
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第8行 第3个单元格的文本:
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第9行有1个单元格{}
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第9行 第0个单元格的文本:获得证书
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第10行有2个单元格{}
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第10行 第0个单元格的文本:获得时间
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第10行 第1个单元格的文本:证书名称
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第11行有2个单元格{}
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第11行 第0个单元格的文本:2022-11-01
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第11行 第1个单元格的文本:CET-6级
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第12行有2个单元格{}
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第12行 第0个单元格的文本:2022-12-01
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第12行 第1个单元格的文本:PMP
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第13行有2个单元格{}
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第13行 第0个单元格的文本:
2022-11-25 16:49:55.744 INFO ReadWordHandler : 第13行 第1个单元格的文本:
图片输出
8 如何将读取的文本转化为我们所需的数据对象
在这份word中
教育经历 和获得证书 这2项 数据是动态的 也就是说他们的内容行数量不固定 需要一套识别逻辑
其他数据都是有固定的位置的
- 如果根据行列索引来取数 那么就很麻烦要一一对应 而且一旦增加删除行 按行列索引取数据就乱了 需要重新设定索引值
- 如果根据左侧单元格标题 来取右侧字段值 那么就会有可能因为其他单元格的值 使用了目标单元格的标题名 而导致数据读取错位 而且 一旦标题名改了 也需要重新设定目标标题名
- 比对这2种读取 都不是很方便 没办法 两权相害取其轻 以数据的准确性为第一目标 因此针对固定位置的数据读取 采用行列坐标来确定
最终 取数转换为对象的逻辑 可以归纳如下
1.针对表格中左侧单元格为字段名 右侧单元格为字段值 的类型:按照单元格值所在的行列索引取值
2.针对动态行数据的 类似子表格 这个子表 带有1个大标题行 1个小标题行 若干数据行 的类型:按照
a.根据大标题行的下一行的小标题的列索引 确定各字段数据所在列索引
b.根据小标题的行索引之后的数据行 根据列索引转换为对应字段的数据值
c.直到下一行的单元格数量 与 小标题行的单元格数量不一致 终止子表的数据读取
9 读取表格文本与图片的方法封装
package com.example.support.entity;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import com.aspose.words.*;
import com.example.support.tool.AsposeTool;
import lombok.Getter;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author LWB
* @Description aspose读取表格数据的策略
*/
public class AsposeReadStrategy {
private Table table;
/**
* 字段值所在的行列坐标与字段名的读取映射
* map的key 为表格中的需要读取数据的行列索引 行索引|列索引 例: 行索引0 列索引0 则坐标为 "0|0"
* map的value 为对象的属性
* */
private Map<String,String> coordKeyMap = new HashMap<>();
/**
* 存储 左侧单元格为字段名 右侧单元格为字段值 的数据map
* 如果需要获取map 直接调用getDataMap
* map的key 为对象的属性
* map的value 为对象的属性值 可能为图片因此用Object
*/
@Getter
private Map<String,Object> dataMap = new HashMap<>();
/**
* 子表的读取映射
* map的key 为表格中的大标题
* map的value 存储的是小标题对应的属性名集合 顺序依次已word中的对应
*/
private Map<String, List<String>> subTableTitleKeyMap = new HashMap<>();
/**
* 存储 子表的数据map
* map的key 为表格中的大标题
* map的value 为 map集合 key-对象的属性名 value-对象的属性值(因为要考虑兼容图片数据的读取,因此value类型为object)
*/
@Getter
private Map<String,List<Map<String,Object>>> subTableDataMap = new HashMap<>();
/**
* 设置坐标对应的属性名
* @param rowIndex 行索引 从0开始
* @param colIndex 列索引 从0开始
* @param key 属性名
* @return
*/
public AsposeReadStrategy setCoordProperty(int rowIndex, int colIndex, String key){
String coord = StrFormatter.format("{}|{}",rowIndex,colIndex);
this.coordKeyMap.put(coord,key);
return this;
}
/**
* 设置子表的标题名与小标题的属性名
* @param subTableTitle 子表的标题名
* @param subTitleKeys 子表的小标题的属性名 注意顺序要与word中的一致
* @return
*/
public AsposeReadStrategy setTableProperty(String subTableTitle, String... subTitleKeys){
this.subTableTitleKeyMap.put(subTableTitle, ListUtil.of(subTitleKeys));
return this;
}
/**
* 数据表的读取策略构造器
* @param table aspose数据表对象 必传
*/
public AsposeReadStrategy(Table table) {
Assert.notNull(table);
this.table = table;
}
/**
* 解析表格数据到对应的属性名
*/
public AsposeReadStrategy parse(){
/**
* 表格行列坐标 对应的数据 按逐行解析时的顺序排序
* key:行索引|列索引 例: 行索引0 列索引0 则坐标为 "0|0"
* value:单元格数据 2种类型 string-文本 byte[]-图片字节数组
*/
Map<String,Object> rowDataMap = new LinkedHashMap<>();
RowCollection rows = table.getRows();
int rowCount = rows.getCount();
//读取所有表格行
for (int i = 0; i < rowCount; i++) {
int rowIndex = i;
Row row = rows.get(i);
CellCollection cells = row.getCells();
int cellCount = cells.getCount();
for (int j = 0; j < cellCount; j++) {
int cellIndex = j;
String coord = StrFormatter.format("{}|{}",rowIndex,cellIndex);
Cell cell = cells.get(j);
//检测单元格种的图片数量
NodeCollection shapeNodes = cell.getChildNodes(NodeType.SHAPE, true);
if(shapeNodes.getCount() > 0){//图片
rowDataMap.put(coord, AsposeTool.getImg(cell));
}else{//文本
rowDataMap.put(coord, AsposeTool.getText(cell));
}
}
}
//遍历读取到的单元格数据 层层筛选所需的数据存储到对应容器
Set<String> coordSet = rowDataMap.keySet();
rowDataMap.forEach((coord,value) -> {
//解析左右对应单元格数据
if(null != coordKeyMap && coordKeyMap.containsKey(coord)){
String key = coordKeyMap.get(coord);
dataMap.put(key,value);
}
//解析子表数据 先确定单元格值为字符串 再匹配大标题
if(null != subTableTitleKeyMap && (value instanceof String) && subTableTitleKeyMap.containsKey((String)value)){
String bigTitle = (String) value;
List<String> subTitleKeyList = subTableTitleKeyMap.get(bigTitle);
/** 存放小标题的列索引和属性名映射 */
Map<String,String> subTitleIndexKeyMap = new HashMap<>();
for (int i = 0; i < subTitleKeyList.size(); i++) {
subTitleIndexKeyMap.put(String.valueOf(i),subTitleKeyList.get(i));
}
int bigTitleRowIndex = Integer.valueOf(StrUtil.subBefore(coord,"|",false));
int subTitleSize = subTitleKeyList.size();//小标题行的单元格数量
//捕捉到大标题后 取出下一行小标题之后的数据
int nextRowIndexStart = bigTitleRowIndex + 1;//小标题的行索引
//直到下一行的单元格数量 与 小标题行的单元格数量不一致 或者 没有下一行时 终止子表的数据读取
//存储子表的数据
List<Map<String,Object>> subTableDataList = new ArrayList<>();
while (true){
nextRowIndexStart ++;
String curRowCoordHead = nextRowIndexStart + "|";
List<String> curRowCoordList = coordSet.parallelStream().filter(a -> a.startsWith(curRowCoordHead)).collect(Collectors.toList());
int curRowCellCount = curRowCoordList.size();
if(curRowCellCount != subTitleSize || curRowCellCount < 1) break;
Map<String,Object> dataMap = new HashMap<>();
for (String curCoord : curRowCoordList) {
String colIndex = StrUtil.subAfter(curCoord, "|",false);
String key = subTitleIndexKeyMap.get(colIndex);
dataMap.put(key,rowDataMap.get(curCoord));
}
subTableDataList.add(dataMap);
}
subTableDataMap.put(bigTitle,subTableDataList);
}
});
return this;
}
/**
* 获取解析后的对象
* @param clz 需要将dataMap映射为的对象类
* @param <T>
* @return
*/
public <T> T getDataBean(Class<T> clz){
return BeanUtil.toBean(this.dataMap, clz);
}
}
10. 测试读取
读取的文档
发起请求
读取方法
@SneakyThrows
@PostMapping("readTableFinal")
public Ret readTableFinal(MultipartFile wordFile){
try(InputStream inputStream = wordFile.getInputStream()){
Document doc = new Document(inputStream);
//获取所有的表格
NodeCollection tableNodes = doc.getChildNodes(NodeType.TABLE, true);
int tableCount = tableNodes.getCount();
for (int i = 0; i < tableCount; i++) {
if(0 == i){
Table table = (Table)tableNodes.get(i);
AsposeReadStrategy readStrategy = new AsposeReadStrategy(table);
readStrategy.setCoordProperty(0,1,"name")
.setCoordProperty(0,2,"photos")
.setCoordProperty(1,1,"idNo")
.setCoordProperty(2,1,"tel")
.setCoordProperty(3,1,"addr")
.setCoordProperty(3,3,"topEdu")
.setTableProperty("教育经历","startTime","endTime","school","major")
.setTableProperty("获得证书","gainTime","certificateName")
.parse();
Map<String, Object> dataMap = readStrategy.getDataMap();
PersonInfo dataBean = readStrategy.getDataBean(PersonInfo.class);
Map<String, List<Map<String, Object>>> subTableDataMap = readStrategy.getSubTableDataMap();
//演示map转为对象 subTableDataMap中的数据 也可以通过以下方法转换为所需对象
PersonInfo personInfo = BeanUtil.toBean(dataMap, PersonInfo.class);
//观察照片的提取结果
List<byte[]> photos = personInfo.getPhotos();
for (byte[] photoBytes : photos) {
FileUtil.writeBytes(photoBytes,RandomUtil.randomString(3)+".jpg");
}
int j = 0;
}
}
}
return Ret.success();
}
package com.example.support.entity;
import lombok.Data;
import java.util.List;
/**
* @author LWB
* @Description
*/
@Data
public class PersonInfo {
private String name;
private List<byte[]> photos;
private String idNo;
private String tel;
private String addr;
private String topEdu;
}
读取结果
在int j = 0 处打断电观察数据读取解析的结果
ok 至此 读取word的功能 基本可以满足大多数需求了