Excel的动态解析+MybatisPlus+Springboot
(省去了手动录入数据)主要用于前端动态渲染
将Excel分为结构数据、业务数据解析为 JSON,分别存储在数据库中
- 结构数据就是指excel表的表头部分,
- 业务数据就是正文部分的数据
1、pom依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.5</version>
</dependency>
2、实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CellNode {
private String id;
private String pId;
private Integer row;
private Integer column;
CellAddress address;
private Integer mergeCellCol;
private Integer mergeCellRow;
private String value;
private List<CellNode> children;
}
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("excel")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Excel {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@NotEmpty(message = "catalog为必填")
private String catalog;
private String header;
private String data;
private String excelName;
@JsonIgnore
private Integer endHeaderRow;
@JsonIgnore
private Integer endDataRow;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate = new Date();
}
3、工具类
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.piesat.kdlsnatdis.entity.vo.CellNode;
import com.piesat.kdlsnatdis.enums.Constants;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.ss.util.CellRangeAddress;
import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
public class ReadExcelUtils {
public static void main(String[] args) {
String path = "E:\\QMDownload\\AppData\\Firefox" + File.separator + "调查XXX况表.xls";
try {
List<CellNode> header = getHeader(5, path);
header.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
}
}
public static List<CellNode> getHeader(Integer rows, String excelPath) throws Exception {
FileInputStream inputStream = new FileInputStream(excelPath);
HSSFWorkbook workbook = new HSSFWorkbook(inputStream);
HSSFSheet sheet = workbook.getSheetAt(0);
List<CellNode> nodeList = new CopyOnWriteArrayList<>();
int row = 1;
int allColumn = 0;
int mergerNode = 0;
Boolean flag = false;
try {
for (Row next : sheet) {
for (Cell cell : next) {
String value = getCellValue(cell);
CellAddress address = cell.getAddress();
int lie = address.getColumn();
int hang = address.getRow();
int mergerNumCol = getMergerCellRegionCol(sheet, hang, lie);
int mergerNumRow = getMergerCellRegionRow(sheet, hang, lie);
if (mergerNumCol == 0 && mergerNumRow == 0) {
mergerNumCol = 1;
}
if (mergerNode == 0 && mergerNumCol > 1) {
mergerNode = mergerNumCol - 1;
}
if (row != (hang + 1)) {
row++;
}
if ((mergerNode != 0) && (hang + 1) == row && StringUtils.isBlank(value)) {
mergerNode--;
continue;
}
if (row == 1) {
nodeList.add(new CellNode(IdUtil.simpleUUID(), "0", hang + 1, lie + 1, address, mergerNumCol, mergerNumRow, value, null));
allColumn += mergerNumCol;
} else {
for (CellNode i : nodeList) {
Integer startCol = i.getColumn();
int lengthCol = i.getColumn() + i.getMergeCellCol() - 1;
if (StringUtils.isNotBlank(value) && startCol <= (lie + 1) && lengthCol >= (lie + 1)) {
List<CellNode> resList = nodeList.stream()
.filter(t -> ((t.getColumn() + t.getMergeCellCol() - 1) >= (lie + 1) && (t.getColumn() <= (lie + 1))))
.sorted(Comparator.comparing(CellNode::getRow).reversed())
.collect(Collectors.toList());
if (resList.size() > 0) {
nodeList.add(new CellNode(IdUtil.simpleUUID(), resList.get(0).getId(), hang + 1, lie + 1, address, mergerNumCol, mergerNumRow, value, null));
break;
}
}
}
}
}
if (row == (rows - 1)) {
inputStream.close();
return nodeList;
}
}
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
inputStream.close();
}
inputStream.close();
return null;
}
public static List<List<String>> getData(Integer startRow, Integer endRow, String excelPath) throws Exception {
FileInputStream inputStream = new FileInputStream(excelPath);
HSSFWorkbook workbook = new HSSFWorkbook(inputStream);
HSSFSheet sheet = workbook.getSheetAt(0);
List<List<String>> list = new CopyOnWriteArrayList<>();
List<CellNode> nodeList = new CopyOnWriteArrayList<>();
int row = startRow;
for (Row next : sheet) {
List<String> data = new CopyOnWriteArrayList<>();
int rowNum = next.getRowNum();
if (rowNum < startRow - 1) {
continue;
}
if (rowNum > endRow - 1) {
inputStream.close();
return list;
}
next.forEach(i -> {
try {
String value = getCellValue(i);
CellAddress address = i.getAddress();
int lie = address.getColumn();
int hang = address.getRow();
int mergerNumRow = getMergerCellRegionRow(sheet, hang, lie);
if ("".equals(value)) {
for (CellNode t : nodeList) {
if (t.getRow() == hang && t.getColumn() == (lie + 1)) {
value = t.getValue();
nodeList.add(new CellNode("", "", hang + 1, lie + 1, address, 1, mergerNumRow, value, null));
}
}
}
nodeList.add(new CellNode("", "", hang + 1, lie + 1, address, 1, mergerNumRow, value, null));
data.add(value);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
});
list.add(data);
}
inputStream.close();
return list;
}
private static String getCellValue(Cell cell) {
String value;
switch (cell.getCellType().name()) {
case "NUMERIC":
if (DateUtil.isCellDateFormatted(cell)) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
value = sdf.format(DateUtil.getJavaDate(cell.
getNumericCellValue()));
break;
} else {
double cellNumericCellValue = cell.getNumericCellValue();
value = NumberUtil.toStr(cellNumericCellValue);
}
break;
case "STRING":
String cellValue = cell.getStringCellValue();
value = cellValue.replace(Constants.YMD_HMS, "")
.replace(Constants.YMD, "")
.replace(Constants.FBT, "");
break;
case "BOOLEAN":
value = cell.getBooleanCellValue() + "";
break;
case "FORMULA":
value = cell.getCellFormula() + "";
break;
case "BLANK":
value = "";
break;
case "ERROR":
value = "非法字符";
break;
default:
value = "未知类型";
break;
}
return value;
}
private static int getMergerCellRegionCol(HSSFSheet sheet, int cellRow, int cellCol) throws Throwable {
int retVal = 0;
int sheetMergerCount = sheet.getNumMergedRegions();
for (int i = 0; i < sheetMergerCount; i++) {
CellRangeAddress cra = sheet.getMergedRegion(i);
int firstRow = cra.getFirstRow();
int firstCol = cra.getFirstColumn();
int lastRow = cra.getLastRow();
int lastCol = cra.getLastColumn();
if (cellRow >= firstRow && cellRow <= lastRow) {
if (cellCol >= firstCol && cellCol <= lastCol) {
retVal = lastCol - firstCol + 1;
break;
}
}
}
return retVal;
}
private static int getMergerCellRegionRow(HSSFSheet sheet, int cellRow, int cellCol) throws Throwable {
int retVal = 0;
int sheetMergerCount = sheet.getNumMergedRegions();
for (int i = 0; i < sheetMergerCount; i++) {
CellRangeAddress cra = sheet.getMergedRegion(i);
int firstRow = cra.getFirstRow();
int firstCol = cra.getFirstColumn();
int lastRow = cra.getLastRow();
int lastCol = cra.getLastColumn();
if (cellCol >= firstCol && cellCol <= lastCol) {
if (cellRow >= firstRow && cellRow <= lastRow) {
retVal = lastRow - firstRow + 1;
break;
}
}
}
return retVal;
}
}
4、Dao层
@Mapper
public interface ExcelMapper extends BaseMapper<Excel> {
}
5、Service层
public interface ExcelService extends IService<Excel> {
Boolean readExcel(String path, Integer rows, Integer endRows, String catalog);
}
@Service
public class ExcelServiceImpl extends ServiceImpl<ExcelMapper, Excel> implements ExcelService {
@Resource
private ExcelMapper excelMapper;
@Override
public Boolean readExcel(String path, Integer rows, Integer endRows, String catalog) {
Excel excel = new Excel();
try {
List<CellNode> header = ReadExcelUtils.getHeader(rows, path);
if (header != null) {
List<CellNode> tree = buildTree(header);
String headerStr = JSON.toJSONString(tree);
excel.setHeader(headerStr);
} else {
return false;
}
List<List<String>> data = ReadExcelUtils.getData(rows, endRows, path);
String dataStr = JSON.toJSONString(data);
excel.setData(dataStr);
excel.setCatalog(catalog);
excel.setEndHeaderRow(rows);
excel.setEndDataRow(endRows);
File file = new File(path);
String fileName = file.getName();
excel.setExcelName(fileName);
return excelMapper.insert(excel) == 1;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public List<CellNode> buildTree(List<CellNode> pidList) {
Map<String, List<CellNode>> pidListMap = pidList.stream().collect(Collectors.groupingBy(CellNode::getPId));
pidList.forEach(i -> {
i.setChildren(pidListMap.get(i.getId()));
});
return pidListMap.get("0");
}
}
6、Controller
@RestController
@RequestMapping("/excel")
public class ExcelController {
private final Logger log = LoggerFactory.getLogger(NdZrzhServiceImpl.class);
@Autowired
private ExcelService excelService;
@PostMapping("/read")
public Result<?> addJsonToSql(@RequestBody Map<String, Object> map) {
int rows;
int endRows;
if (Objects.isNull(map.get("path")) || StringUtils.isBlank(map.get("path").toString())) {
return new Result<>(StatusCode.ERROR, "'path'必须必填");
}
if (Objects.isNull(map.get("catalog")) || StringUtils.isBlank(map.get("catalog").toString())) {
return new Result<>(StatusCode.ERROR, "'catalog'必须必填");
}
if (map.get("rows") != null) {
rows = Integer.parseInt(map.get("rows").toString());
if (rows < 0) {
return new Result<>(StatusCode.ERROR, "rows必须>0");
}
} else {
return new Result<>(StatusCode.ERROR, "rows必填");
}
if (map.get("endRows") != null) {
endRows = Integer.parseInt(map.get("endRows").toString());
if (endRows < 0) {
return new Result<>(StatusCode.ERROR, "endRows必须>0");
}
} else {
return new Result<>(StatusCode.ERROR, "endRows必填");
}
String path = map.get("path").toString();
String catalog = map.get("catalog").toString();
if (excelService.readExcel(path, rows, endRows, catalog)) {
return new Result<>(StatusCode.SUCCESS, "excel解析成功");
} else {
return new Result<>(StatusCode.ERROR, "excel解析失败");
}
}
}
7、Postman 测试
- 参数:catalog,为文件名的简写。
- 参数:rows 和 endrows 的取值说明
|