【easypoi 一对多导入解决方案】
easypoi 一对多导入解决方案
- 1.需求
- 2.复现问题
- 2.1校验时获取不到一对多中多的完整数据
- 2.2控制台报错 Cannot add merged region B5:B7 to sheet because it overlaps with an existing merged region (B3:B5).
- 3.如何解决
- 第二个问题处理: Cannot add merged region B5:B7 to sheet because it overlaps with an existing merged region (B3:B5).
- 第一个问题处理,校验时获取不到一对多中多的完整数据
- 3 完整环境
- 3.1 ImportController
- 3.2 MyExcelImportService
- 3.3 Maven 依赖
- 4.git 完整代码
1.需求
- 把如图的数据导入,
(1)校验姓名长度不能大于 100
(2)校验每一行次数 + 费用之和不能大于等于 10
(3)提示哪一行报错了 - 首先是一个一对多的导入,其次提示哪一行报错使用 ExcelImportUtil.importExcelMore().getFailWorkbook() 方法生成一个问题 excel 到服务器本地,再写一个下载失败文档的接口,让用户下载即可;
- 但是在在导入的时候报错,Cannot add merged region B5:B7 to sheet because it overlaps with an existing merged region (B3:B5).,且在校验
每一行次数 + 费用之和不能大于等于 10
时只能获取第一行的数据。
2.复现问题
上代码
package com.example.myeasypoi;import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.excel.entity.result.ExcelVerifyHandlerResult;
import cn.afterturn.easypoi.handler.inter.IExcelDataModel;
import cn.afterturn.easypoi.handler.inter.IExcelModel;
import cn.afterturn.easypoi.handler.inter.IExcelVerifyHandler;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.hibernate.validator.constraints.Length;
import org.springframework.web.bind.annotation.RestController;import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.util.Date;
import java.util.List;@RestController
public class ImportController {public static void main(String[] args) throws Exception {BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/study/code_source/my-easypoi/src/main/resources/ExcelExportTemplateMyNestedLoop.xlsx"));ImportParams importParams = new ImportParams();importParams.setHeadRows(2);importParams.setNeedVerify(true);importParams.setVerifyHandler(new MyVerifyHandler());ExcelImportResult<Object> result = ExcelImportUtil.importExcelMore(bis, MyPojo.class, importParams);System.out.println(result);}@Datapublic static class MyPojo extends Traffic implements IExcelDataModel, IExcelModel{/*** 行号*/private int rowNum;/*** 错误消息*/private String errorMsg;@Overridepublic String getErrorMsg() {return errorMsg;}@Overridepublic void setErrorMsg(String s) {this.errorMsg =s;}@Overridepublic Integer getRowNum() {return rowNum;}@Overridepublic void setRowNum(Integer rowNum) {this.rowNum = rowNum;}}public static class MyVerifyHandler implements IExcelVerifyHandler<MyPojo> {@Overridepublic ExcelVerifyHandlerResult verifyHandler(MyPojo myPojo) {StringBuilder sb = new StringBuilder();List<TrafficDetail> shareBikes = myPojo.getShareBikes();List<TrafficDetail> subways = myPojo.getSubways();if (CollectionUtils.isNotEmpty(shareBikes)){shareBikes.forEach(shareBike -> {if(getSum(shareBike.getNumber(),shareBike.getCost())>=10){sb.append("共享单车次数和费用之和大于 10");}});}if(CollectionUtils.isNotEmpty(subways)){subways.forEach(subway -> {if(getSum(subway.getNumber(),subway.getCost()) >=10){sb.append("地铁次数和费用之和大于 10");}});}if(sb.length()!= 0){return new ExcelVerifyHandlerResult(false,sb.toString());}return new ExcelVerifyHandlerResult(true);}private int getSum(Integer a ,Integer b){return (a == null ? 0 : a) + (b == null ? 0 : b);}}@Data@AllArgsConstructor@NoArgsConstructorpublic static class Traffic{@Excel(name = "序号")private Integer id;@Excel(name = "姓名")@Length(max = 100,message = "姓名长度不能大于 100")private String name;@Excel(name = "日期",format = "yyyy-MM-dd")private Date date;@ExcelCollection(name = "共享单车")private List<TrafficDetail> shareBikes;@ExcelCollection(name = "地铁")private List<TrafficDetail> subways;}@Data@AllArgsConstructor@NoArgsConstructorpublic static class TrafficDetail{@Excel(name = "次数")private Integer number;@Excel(name = "费用")private Integer cost;}
}
2.1校验时获取不到一对多中多的完整数据
2.2控制台报错 Cannot add merged region B5:B7 to sheet because it overlaps with an existing merged region (B3:B5).
easypoi 是先校验,再获取值,所以第二个报错在我放开第一个断点后出现。
3.如何解决
第二个问题处理: Cannot add merged region B5:B7 to sheet because it overlaps with an existing merged region (B3:B5).
首先 ImportExcelMore 实际调用的 new ExcelImportService().importExcelByIs(inputstream, pojoClass, params, true);
查看代码逻辑,是因为ExcelImportService.removeSuperfluousRows 错误
如何改呢,上代码,就是继承 ExcelImportService 类,重写这部分代码,然后在调研时候直接用自己写的 类;
/*** Copyright 2013-2015 JueYue (qrb.jueyue@gmail.com)* <p>* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except* in compliance with the License. You may obtain a copy of the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software distributed under the License* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express* or implied. See the License for the specific language governing permissions and limitations under* the License.*/
package com.example.myeasypoi;import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelCollectionParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelImportEntity;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.excel.entity.result.ExcelVerifyHandlerResult;
import cn.afterturn.easypoi.entity.BaseTypeConstants;
import cn.afterturn.easypoi.excel.imports.CellValueService;
import cn.afterturn.easypoi.excel.imports.ExcelImportService;
import cn.afterturn.easypoi.excel.imports.base.ImportBaseService;
import cn.afterturn.easypoi.excel.imports.recursive.ExcelImportForkJoinWork;
import cn.afterturn.easypoi.exception.excel.ExcelImportException;
import cn.afterturn.easypoi.exception.excel.enums.ExcelImportEnum;
import cn.afterturn.easypoi.handler.inter.IExcelDataModel;
import cn.afterturn.easypoi.handler.inter.IExcelModel;
import cn.afterturn.easypoi.util.PoiCellUtil;
import cn.afterturn.easypoi.util.PoiPublicUtil;
import cn.afterturn.easypoi.util.PoiReflectorUtil;
import cn.afterturn.easypoi.util.PoiValidationUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ForkJoinPool;/*** Excel 导入服务** @author JueYue 2014年6月26日 下午9:20:51*/
@SuppressWarnings({"rawtypes", "unchecked", "hiding"})
public class MyExcelImportService extends ExcelImportService {private final static Logger LOGGER = LoggerFactory.getLogger(MyExcelImportService.class);private CellValueService cellValueServer;private boolean verifyFail = false;/*** 异常数据styler*/private CellStyle errorCellStyle;private List<Row> successRow;private List<Row> failRow;private List failCollection;public MyExcelImportService() {successRow = new ArrayList<Row>();failRow = new ArrayList<Row>();failCollection = new ArrayList();this.cellValueServer = new CellValueService();}/**** 向List里面继续添加元素** @param object* @param param* @param row* @param titlemap* @param targetId* @param pictures* @param params*/public void addListContinue(Object object, ExcelCollectionParams param, Row row,Map<Integer, String> titlemap, String targetId,Map<String, PictureData> pictures,ImportParams params, StringBuilder errorMsg) throws Exception {Collection collection = (Collection) PoiReflectorUtil.fromCache(object.getClass()).getValue(object, param.getName());Object entity = PoiPublicUtil.createObject(param.getType(), targetId);if (entity instanceof IExcelDataModel) {((IExcelDataModel) entity).setRowNum(row.getRowNum());}String picId;// 是否需要加上这个对象boolean isUsed = false;for (int i = row.getFirstCellNum(); i < titlemap.size(); i++) {Cell cell = row.getCell(i);String titleString = (String) titlemap.get(i);if (param.getExcelParams().containsKey(titleString)) {if (param.getExcelParams().get(titleString).getType() == BaseTypeConstants.IMAGE_TYPE) {picId = row.getRowNum() + "_" + i;saveImage(entity, picId, param.getExcelParams(), titleString, pictures, params);} else {try {saveFieldValue(params, entity, cell, param.getExcelParams(), titleString, row);} catch (ExcelImportException e) {// 如果需要去校验就忽略,这个错误,继续执行if (params.isNeedVerify() && ExcelImportEnum.GET_VALUE_ERROR.equals(e.getType())) {errorMsg.append(" ").append(titleString).append(ExcelImportEnum.GET_VALUE_ERROR.getMsg());}}}isUsed = true;}}if (isUsed) {collection.add(entity);}}/*** 获取key的值,针对不同类型获取不同的值** @author JueYue 2013-11-21*/private String getKeyValue(Cell cell) {Object obj = PoiCellUtil.getCellValue(cell);return obj == null ? null : obj.toString().trim();}/*** 获取保存的真实路径*/private String getSaveUrl(ExcelImportEntity excelImportEntity, Object object) throws Exception {String url = "";if (ExcelImportEntity.IMG_SAVE_PATH.equals(excelImportEntity.getSaveUrl())) {if (excelImportEntity.getMethods() != null&& excelImportEntity.getMethods().size() > 0) {object = getFieldBySomeMethod(excelImportEntity.getMethods(), object);}url = object.getClass().getName().split("\\.")[object.getClass().getName().split("\\.").length - 1];return excelImportEntity.getSaveUrl() + File.separator + url;}return excelImportEntity.getSaveUrl();}private <T> List<T> importExcel(Collection<T> result, Sheet sheet, Class<?> pojoClass,ImportParams params,Map<String, PictureData> pictures) throws Exception {List collection = new ArrayList();Map<String, ExcelImportEntity> excelParams = new HashMap<>();List<ExcelCollectionParams> excelCollection = new ArrayList<>();String targetId = null;i18nHandler = params.getI18nHandler();boolean isMap = Map.class.equals(pojoClass);if (!isMap) {Field[] fileds = PoiPublicUtil.getClassFields(pojoClass);ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);if (etarget != null) {targetId = etarget.value();}getAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null, null);}Iterator<Row> rows = sheet.rowIterator();for (int j = 0; j < params.getTitleRows(); j++) {rows.next();}Map<Integer, String> titlemap = getTitleMap(rows, params, excelCollection, excelParams);checkIsValidTemplate(titlemap, excelParams, params, excelCollection);Row row = null;Object object = null;String picId;int readRow = 1;//跳过无效行for (int i = 0; i < params.getStartRows(); i++) {rows.next();}//判断index 和集合,集合情况默认为第一列if (excelCollection.size() > 0 && params.getKeyIndex() == null) {params.setKeyIndex(0);}int endRow = sheet.getLastRowNum() - params.getLastOfInvalidRow();if (params.getReadRows() > 0) {endRow = Math.min(params.getReadRows(), endRow);}if (params.isConcurrentTask()) {ForkJoinPool forkJoinPool = new ForkJoinPool();ExcelImportForkJoinWork task = new ExcelImportForkJoinWork(params.getStartRows() + params.getHeadRows() + params.getTitleRows(), endRow, sheet, params, pojoClass, this, targetId, titlemap, excelParams);ExcelImportResult forkJoinResult = forkJoinPool.invoke(task);collection = forkJoinResult.getList();failCollection = forkJoinResult.getFailList();} else {StringBuilder errorMsg;while (rows.hasNext()) {row = rows.next();// Fix 如果row为无效行时候跳出if (row.getRowNum() > endRow) {break;}/* 如果当前行的单元格都是无效的,那就继续下一行 */if (row.getLastCellNum()<0) {continue;}if(isMap && object != null) {((Map) object).put("excelRowNum", row.getRowNum());}errorMsg = new StringBuilder();// 判断是集合元素还是不是集合元素,如果是就继续加入这个集合,不是就创建新的对象// keyIndex 如果为空就不处理,仍然处理这一行if (params.getKeyIndex() != null&& (row.getCell(params.getKeyIndex()) == null|| StringUtils.isEmpty(getKeyValue(row.getCell(params.getKeyIndex()))))&& object != null) {for (ExcelCollectionParams param : excelCollection) {addListContinue(object, param, row, titlemap, targetId, pictures, params, errorMsg);}} else {object = PoiPublicUtil.createObject(pojoClass, targetId);try {Set<Integer> keys = titlemap.keySet();for (Integer cn : keys) {Cell cell = row.getCell(cn);String titleString = (String) titlemap.get(cn);if (excelParams.containsKey(titleString) || isMap) {if (excelParams.get(titleString) != null&& excelParams.get(titleString).getType() == BaseTypeConstants.IMAGE_TYPE) {picId = row.getRowNum() + "_" + cn;saveImage(object, picId, excelParams, titleString, pictures,params);} else {try {saveFieldValue(params, object, cell, excelParams, titleString, row);} catch (ExcelImportException e) {// 如果需要去校验就忽略,这个错误,继续执行if (params.isNeedVerify() && ExcelImportEnum.GET_VALUE_ERROR.equals(e.getType())) {errorMsg.append(" ").append(titleString).append(ExcelImportEnum.GET_VALUE_ERROR.getMsg());}}}}}//for (int i = row.getFirstCellNum(), le = titlemap.size(); i < le; i++) {//}if (object instanceof IExcelDataModel) {((IExcelDataModel) object).setRowNum(row.getRowNum());}for (ExcelCollectionParams param : excelCollection) {addListContinue(object, param, row, titlemap, targetId, pictures, params, errorMsg);}if (verifyingDataValidity(object, row, params, isMap, errorMsg)) {collection.add(object);} else {failCollection.add(object);}} catch (ExcelImportException e) {LOGGER.error("excel import error , row num:{},obj:{}", readRow, ReflectionToStringBuilder.toString(object));if (!e.getType().equals(ExcelImportEnum.VERIFY_ERROR)) {throw new ExcelImportException(e.getType(), e);}} catch (Exception e) {LOGGER.error("excel import error , row num:{},obj:{}", readRow, ReflectionToStringBuilder.toString(object));throw new RuntimeException(e);}}readRow++;}}return collection;}/*** 校验数据合法性*/public boolean verifyingDataValidity(Object object, Row row, ImportParams params,boolean isMap, StringBuilder fieldErrorMsg) {boolean isAdd = true;Cell cell = null;if (params.isNeedVerify()) {String errorMsg = PoiValidationUtil.validation(object, params.getVerifyGroup());if (StringUtils.isNotEmpty(errorMsg)) {cell = row.createCell(row.getLastCellNum());cell.setCellValue(errorMsg);if (object instanceof IExcelModel) {IExcelModel model = (IExcelModel) object;model.setErrorMsg(errorMsg);}isAdd = false;verifyFail = true;}}if (params.getVerifyHandler() != null) {ExcelVerifyHandlerResult result = params.getVerifyHandler().verifyHandler(object);if (!result.isSuccess()) {if (cell == null) {cell = row.createCell(row.getLastCellNum());}cell.setCellValue((StringUtils.isNoneBlank(cell.getStringCellValue())? cell.getStringCellValue() + "," : "") + result.getMsg());if (object instanceof IExcelModel) {IExcelModel model = (IExcelModel) object;model.setErrorMsg((StringUtils.isNoneBlank(model.getErrorMsg())? model.getErrorMsg() + "," : "") + result.getMsg());}isAdd = false;verifyFail = true;}}if ((params.isNeedVerify() || params.getVerifyHandler() != null) && fieldErrorMsg.length() > 0) {if (object instanceof IExcelModel) {IExcelModel model = (IExcelModel) object;model.setErrorMsg((StringUtils.isNoneBlank(model.getErrorMsg())? model.getErrorMsg() + "," : "") + fieldErrorMsg.toString());}if (cell == null) {cell = row.createCell(row.getLastCellNum());}cell.setCellValue((StringUtils.isNoneBlank(cell.getStringCellValue())? cell.getStringCellValue() + "," : "") + fieldErrorMsg.toString());isAdd = false;verifyFail = true;}if (cell != null) {cell.setCellStyle(errorCellStyle);failRow.add(row);if(isMap) {((Map) object).put("excelErrorMsg", cell.getStringCellValue());}} else {successRow.add(row);}return isAdd;}/*** 获取表格字段列名对应信息*/private Map<Integer, String> getTitleMap(Iterator<Row> rows, ImportParams params,List<ExcelCollectionParams> excelCollection,Map<String, ExcelImportEntity> excelParams) {Map<Integer, String> titlemap = new LinkedHashMap<Integer, String>();Iterator<Cell> cellTitle;String collectionName = null;ExcelCollectionParams collectionParams = null;Row row = null;for (int j = 0; j < params.getHeadRows(); j++) {row = rows.next();if (row == null) {continue;}cellTitle = row.cellIterator();while (cellTitle.hasNext()) {Cell cell = cellTitle.next();String value = getKeyValue(cell);value = value.replace("\n", "");int i = cell.getColumnIndex();//用以支持重名导入if (StringUtils.isNotEmpty(value)) {if (titlemap.containsKey(i)) {collectionName = titlemap.get(i);collectionParams = getCollectionParams(excelCollection, collectionName);titlemap.put(i, collectionName + "_" + value);} else if (StringUtils.isNotEmpty(collectionName) && collectionParams != null&& collectionParams.getExcelParams().containsKey(collectionName + "_" + value)) {titlemap.put(i, collectionName + "_" + value);} else {collectionName = null;collectionParams = null;}if (StringUtils.isEmpty(collectionName)) {titlemap.put(i, value);}}}}// 处理指定列的情况Set<String> keys = excelParams.keySet();for (String key : keys) {if (key.startsWith("FIXED_")) {String[] arr = key.split("_");titlemap.put(Integer.parseInt(arr[1]), key);}}return titlemap;}/*** 获取这个名称对应的集合信息*/private ExcelCollectionParams getCollectionParams(List<ExcelCollectionParams> excelCollection,String collectionName) {for (ExcelCollectionParams excelCollectionParams : excelCollection) {if (collectionName.equals(excelCollectionParams.getExcelName())) {return excelCollectionParams;}}return null;}/*** Excel 导入 field 字段类型 Integer,Long,Double,Date,String,Boolean*/public ExcelImportResult importExcelByIs(InputStream inputstream, Class<?> pojoClass,ImportParams params, boolean needMore) throws Exception {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Excel import start ,class is {}", pojoClass);}List<T> result = new ArrayList<T>();ByteArrayOutputStream baos = new ByteArrayOutputStream();ExcelImportResult importResult;try {byte[] buffer = new byte[1024];int len;while ((len = inputstream.read(buffer)) > -1) {baos.write(buffer, 0, len);}baos.flush();InputStream userIs = new ByteArrayInputStream(baos.toByteArray());if (LOGGER.isDebugEnabled()) {LOGGER.debug("Excel clone success");}Workbook book = WorkbookFactory.create(userIs);boolean isXSSFWorkbook = !(book instanceof HSSFWorkbook);if (LOGGER.isDebugEnabled()) {LOGGER.debug("Workbook create success");}importResult = new ExcelImportResult();createErrorCellStyle(book);Map<String, PictureData> pictures;for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()+ params.getSheetNum(); i++) {if (LOGGER.isDebugEnabled()) {LOGGER.debug(" start to read excel by is ,startTime is {}", new Date());}if (isXSSFWorkbook) {pictures = PoiPublicUtil.getSheetPictrues07((XSSFSheet) book.getSheetAt(i),(XSSFWorkbook) book);} else {pictures = PoiPublicUtil.getSheetPictrues03((HSSFSheet) book.getSheetAt(i),(HSSFWorkbook) book);}if (LOGGER.isDebugEnabled()) {LOGGER.debug(" end to read excel by is ,endTime is {}", new Date());}result.addAll(importExcel(result, book.getSheetAt(i), pojoClass, params, pictures));if (LOGGER.isDebugEnabled()) {LOGGER.debug(" end to read excel list by sheet ,endTime is {}", new Date());}if (params.isReadSingleCell()) {readSingleCell(importResult, book.getSheetAt(i), params);if (LOGGER.isDebugEnabled()) {LOGGER.debug(" read Key-Value ,endTime is {}", System.currentTimeMillis());}}}if (params.isNeedSave()) {saveThisExcel(params, pojoClass, isXSSFWorkbook, book);}importResult.setList(result);if (needMore) {InputStream successIs = new ByteArrayInputStream(baos.toByteArray());try {Workbook successBook = WorkbookFactory.create(successIs);if (params.isVerifyFileSplit()){importResult.setWorkbook(removeSuperfluousRows(successBook, failRow, params));importResult.setFailWorkbook(removeSuperfluousRows(book, successRow, params));} else {importResult.setWorkbook(book);}importResult.setFailList(failCollection);importResult.setVerifyFail(verifyFail);} finally {successIs.close();}}} finally {IOUtils.closeQuietly(baos);}return importResult;}private Workbook removeSuperfluousRows(Workbook book, List<Row> rowList, ImportParams params) {for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()+ params.getSheetNum(); i++) {for (int j = rowList.size() - 1; j >= 0; j--) {if (rowList.get(j).getRowNum() < rowList.get(j).getSheet().getLastRowNum()) {book.getSheetAt(i).shiftRows(rowList.get(j).getRowNum() + 1, rowList.get(j).getSheet().getLastRowNum(), 1);} else if (rowList.get(j).getRowNum() == rowList.get(j).getSheet().getLastRowNum()) {book.getSheetAt(i).createRow(rowList.get(j).getRowNum() + 1);book.getSheetAt(i).shiftRows(rowList.get(j).getRowNum() + 1, rowList.get(j).getSheet().getLastRowNum() + 1, 1);}}}return book;}/*** 按照键值对的方式取得Excel里面的数据*/private void readSingleCell(ExcelImportResult result, Sheet sheet, ImportParams params) {if (result.getMap() == null) {result.setMap(new HashMap<String, Object>());}for (int i = 0; i < params.getTitleRows() + params.getHeadRows() + params.getStartRows(); i++) {getSingleCellValueForRow(result, sheet.getRow(i), params);}for (int i = sheet.getLastRowNum() - params.getLastOfInvalidRow(); i < sheet.getLastRowNum(); i++) {getSingleCellValueForRow(result, sheet.getRow(i), params);}}private void getSingleCellValueForRow(ExcelImportResult result, Row row, ImportParams params) {for (int j = row.getFirstCellNum(), le = row.getLastCellNum(); j < le; j++) {String text = PoiCellUtil.getCellValue(row.getCell(j));if (StringUtils.isNoneBlank(text) && text.endsWith(params.getKeyMark())) {if (result.getMap().containsKey(text)) {if (result.getMap().get(text) instanceof String) {List<String> list = new ArrayList<String>();list.add((String) result.getMap().get(text));result.getMap().put(text, list);}((List) result.getMap().get(text)).add(PoiCellUtil.getCellValue(row.getCell(++j)));} else {result.getMap().put(text, PoiCellUtil.getCellValue(row.getCell(++j)));}}}}/*** 检查是不是合法的模板*/private void checkIsValidTemplate(Map<Integer, String> titlemap,Map<String, ExcelImportEntity> excelParams,ImportParams params,List<ExcelCollectionParams> excelCollection) {if (params.getImportFields() != null) {// 同时校验列顺序if (params.isNeedCheckOrder()) {if (params.getImportFields().length != titlemap.size()) {LOGGER.error("excel列顺序不一致");throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}int i = 0;for (String title : titlemap.values()) {if (!StringUtils.equals(title, params.getImportFields()[i++])) {LOGGER.error("excel列顺序不一致");throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}}} else {for (int i = 0, le = params.getImportFields().length; i < le; i++) {if (!titlemap.containsValue(params.getImportFields()[i])) {throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}}}} else {Collection<ExcelImportEntity> collection = excelParams.values();for (ExcelImportEntity excelImportEntity : collection) {if (excelImportEntity.isImportField()&& !titlemap.containsValue(excelImportEntity.getName())) {LOGGER.error(excelImportEntity.getName() + "必须有,但是没找到");throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}}for (int i = 0, le = excelCollection.size(); i < le; i++) {ExcelCollectionParams collectionparams = excelCollection.get(i);collection = collectionparams.getExcelParams().values();for (ExcelImportEntity excelImportEntity : collection) {if (excelImportEntity.isImportField() && !titlemap.containsValue(collectionparams.getExcelName() + "_" + excelImportEntity.getName())) {throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}}}}}/*** 保存字段值(获取值,校验值,追加错误信息)*/public void saveFieldValue(ImportParams params, Object object, Cell cell,Map<String, ExcelImportEntity> excelParams, String titleString,Row row) throws Exception {Object value = cellValueServer.getValue(params.getDataHandler(), object, cell, excelParams,titleString, params.getDictHandler());if (object instanceof Map) {if (params.getDataHandler() != null) {params.getDataHandler().setMapValue((Map) object, titleString, value);} else {((Map) object).put(titleString, value);}} else {setValues(excelParams.get(titleString), object, value);}}/*** @param object* @param picId* @param excelParams* @param titleString* @param pictures* @param params* @throws Exception*/private void saveImage(Object object, String picId, Map<String, ExcelImportEntity> excelParams,String titleString, Map<String, PictureData> pictures,ImportParams params) throws Exception {if (pictures == null) {return;}PictureData image = pictures.get(picId);if (image == null) {return;}byte[] data = image.getData();String fileName = "pic" + Math.round(Math.random() * 100000000000L);fileName += "." + PoiPublicUtil.getFileExtendName(data);if (excelParams.get(titleString).getSaveType() == 1) {String path = getSaveUrl(excelParams.get(titleString), object);File savefile = new File(path);if (!savefile.exists()) {savefile.mkdirs();}savefile = new File(path + File.separator + fileName);FileOutputStream fos = new FileOutputStream(savefile);try {fos.write(data);} finally {IOUtils.closeQuietly(fos);}setValues(excelParams.get(titleString), object,getSaveUrl(excelParams.get(titleString), object) + File.separator + fileName);} else {setValues(excelParams.get(titleString), object, data);}}private void createErrorCellStyle(Workbook workbook) {errorCellStyle = workbook.createCellStyle();Font font = workbook.createFont();font.setColor(Font.COLOR_RED);errorCellStyle.setFont(font);}}
第一个问题处理,校验时获取不到一对多中多的完整数据
- 当我把 第二个问题处理后,第一个问题在校验的时候还是只能获取一对多的第一个,实在没找到源码,也不想找了,就用一个笨方法处理了,就是把读入的流分成两份,一份用 ExcelImportUtil.importExcel() 不校验获取所有数据,一份用 ExcelImportUtil.importExcelMore() 处理,在设置校验类时,用构造方法的方式放入 ExcelImportUtil.importExcel() 的返回值到校验类中。
- 这样技能够校验了,也能过获取完整的数据了。
3 完整环境
3.1 ImportController
package com.example.myeasypoi;import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.excel.entity.result.ExcelVerifyHandlerResult;
import cn.afterturn.easypoi.handler.inter.IExcelDataModel;
import cn.afterturn.easypoi.handler.inter.IExcelModel;
import cn.afterturn.easypoi.handler.inter.IExcelVerifyHandler;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.Length;
import org.springframework.web.bind.annotation.RestController;import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;@RestController
public class ImportController {public static void main(String[] args) throws Exception {BufferedInputStream bis1 = new BufferedInputStream(new FileInputStream("D:/study/code_source/my-easypoi/src/main/resources/ExcelExportTemplateMyNestedLoop.xlsx"));BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream("D:/study/code_source/my-easypoi/src/main/resources/ExcelExportTemplateMyNestedLoop.xlsx"));//第一份文件,不校验ImportParams importParams1 = new ImportParams();importParams1.setHeadRows(2);List<MyPojo> firstMyPojoList = ExcelImportUtil.importExcel(bis1, MyPojo.class, importParams1);Map<Integer, MyPojo> idMyPojoMap = firstMyPojoList.stream().collect(Collectors.toMap(MyPojo::getId, e -> e));ImportParams importParams2 = new ImportParams();importParams2.setHeadRows(2);importParams2.setNeedVerify(true);importParams2.setVerifyHandler(new MyVerifyHandler(idMyPojoMap));
// ExcelImportResult<Object> result = ExcelImportUtil.importExcelMore(bis, MyPojo.class, importParams);ExcelImportResult<MyPojo> result = new MyExcelImportService().importExcelByIs(bis2, MyPojo.class, importParams2,true);List<MyPojo> failList = result.getFailList();System.out.println(failList);}@Datapublic static class MyPojo extends Traffic implements IExcelDataModel, IExcelModel{/*** 行号*/private int rowNum;/*** 错误消息*/private String errorMsg;@Overridepublic String getErrorMsg() {return errorMsg;}@Overridepublic void setErrorMsg(String s) {this.errorMsg =s;}@Overridepublic Integer getRowNum() {return rowNum;}@Overridepublic void setRowNum(Integer rowNum) {this.rowNum = rowNum;}}public static class MyVerifyHandler implements IExcelVerifyHandler<MyPojo> {Map<Integer,MyPojo> idMyPojoMap;public MyVerifyHandler(Map<Integer, MyPojo> idMyPojoMap) {this.idMyPojoMap = idMyPojoMap;}@Overridepublic ExcelVerifyHandlerResult verifyHandler(MyPojo myPojo) {myPojo = idMyPojoMap.get(myPojo.getId());StringBuilder sb = new StringBuilder();//校验String name = myPojo.getName();if(StringUtils.isNotEmpty(name) && name.length() > 100){sb.append("姓名长度不能超过 100");}List<TrafficDetail> shareBikes = myPojo.getShareBikes();List<TrafficDetail> subways = myPojo.getSubways();if (CollectionUtils.isNotEmpty(shareBikes)){shareBikes.forEach(shareBike -> {if(getSum(shareBike.getNumber(),shareBike.getCost())>=10){sb.append("共享单车次数和费用之和大于 10");}});}if(CollectionUtils.isNotEmpty(subways)){subways.forEach(subway -> {if(getSum(subway.getNumber(),subway.getCost()) >=10){sb.append("地铁次数和费用之和大于 10");}});}ExcelVerifyHandlerResult excelVerifyHandlerResult;if(sb.length()!= 0){excelVerifyHandlerResult= new ExcelVerifyHandlerResult(false,sb.toString());}else {excelVerifyHandlerResult= new ExcelVerifyHandlerResult(true);}return excelVerifyHandlerResult;}private int getSum(Integer a ,Integer b){return (a == null ? 0 : a) + (b == null ? 0 : b);}}@Data@AllArgsConstructor@NoArgsConstructorpublic static class Traffic{@Excel(name = "序号")private Integer id;@Excel(name = "姓名")@Length(max = 100,message = "姓名长度不能大于 100")private String name;@Excel(name = "日期",format = "yyyy-MM-dd")private Date date;@ExcelCollection(name = "共享单车")private List<TrafficDetail> shareBikes;@ExcelCollection(name = "地铁")private List<TrafficDetail> subways;}@Data@AllArgsConstructor@NoArgsConstructorpublic static class TrafficDetail{@Excel(name = "次数")private Integer number;@Excel(name = "费用")private Integer cost;}
}
3.2 MyExcelImportService
/*** Copyright 2013-2015 JueYue (qrb.jueyue@gmail.com)* <p>* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except* in compliance with the License. You may obtain a copy of the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software distributed under the License* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express* or implied. See the License for the specific language governing permissions and limitations under* the License.*/
package com.example.myeasypoi;import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelCollectionParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelImportEntity;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.excel.entity.result.ExcelVerifyHandlerResult;
import cn.afterturn.easypoi.entity.BaseTypeConstants;
import cn.afterturn.easypoi.excel.imports.CellValueService;
import cn.afterturn.easypoi.excel.imports.ExcelImportService;
import cn.afterturn.easypoi.excel.imports.base.ImportBaseService;
import cn.afterturn.easypoi.excel.imports.recursive.ExcelImportForkJoinWork;
import cn.afterturn.easypoi.exception.excel.ExcelImportException;
import cn.afterturn.easypoi.exception.excel.enums.ExcelImportEnum;
import cn.afterturn.easypoi.handler.inter.IExcelDataModel;
import cn.afterturn.easypoi.handler.inter.IExcelModel;
import cn.afterturn.easypoi.util.PoiCellUtil;
import cn.afterturn.easypoi.util.PoiPublicUtil;
import cn.afterturn.easypoi.util.PoiReflectorUtil;
import cn.afterturn.easypoi.util.PoiValidationUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ForkJoinPool;/*** Excel 导入服务** @author JueYue 2014年6月26日 下午9:20:51*/
@SuppressWarnings({"rawtypes", "unchecked", "hiding"})
public class MyExcelImportService extends ExcelImportService {private final static Logger LOGGER = LoggerFactory.getLogger(MyExcelImportService.class);private CellValueService cellValueServer;private boolean verifyFail = false;/*** 异常数据styler*/private CellStyle errorCellStyle;private List<Row> successRow;private List<Row> failRow;private List failCollection;public MyExcelImportService() {successRow = new ArrayList<Row>();failRow = new ArrayList<Row>();failCollection = new ArrayList();this.cellValueServer = new CellValueService();}/**** 向List里面继续添加元素** @param object* @param param* @param row* @param titlemap* @param targetId* @param pictures* @param params*/public void addListContinue(Object object, ExcelCollectionParams param, Row row,Map<Integer, String> titlemap, String targetId,Map<String, PictureData> pictures,ImportParams params, StringBuilder errorMsg) throws Exception {Collection collection = (Collection) PoiReflectorUtil.fromCache(object.getClass()).getValue(object, param.getName());Object entity = PoiPublicUtil.createObject(param.getType(), targetId);if (entity instanceof IExcelDataModel) {((IExcelDataModel) entity).setRowNum(row.getRowNum());}String picId;// 是否需要加上这个对象boolean isUsed = false;for (int i = row.getFirstCellNum(); i < titlemap.size(); i++) {Cell cell = row.getCell(i);String titleString = (String) titlemap.get(i);if (param.getExcelParams().containsKey(titleString)) {if (param.getExcelParams().get(titleString).getType() == BaseTypeConstants.IMAGE_TYPE) {picId = row.getRowNum() + "_" + i;saveImage(entity, picId, param.getExcelParams(), titleString, pictures, params);} else {try {saveFieldValue(params, entity, cell, param.getExcelParams(), titleString, row);} catch (ExcelImportException e) {// 如果需要去校验就忽略,这个错误,继续执行if (params.isNeedVerify() && ExcelImportEnum.GET_VALUE_ERROR.equals(e.getType())) {errorMsg.append(" ").append(titleString).append(ExcelImportEnum.GET_VALUE_ERROR.getMsg());}}}isUsed = true;}}if (isUsed) {collection.add(entity);}}/*** 获取key的值,针对不同类型获取不同的值** @author JueYue 2013-11-21*/private String getKeyValue(Cell cell) {Object obj = PoiCellUtil.getCellValue(cell);return obj == null ? null : obj.toString().trim();}/*** 获取保存的真实路径*/private String getSaveUrl(ExcelImportEntity excelImportEntity, Object object) throws Exception {String url = "";if (ExcelImportEntity.IMG_SAVE_PATH.equals(excelImportEntity.getSaveUrl())) {if (excelImportEntity.getMethods() != null&& excelImportEntity.getMethods().size() > 0) {object = getFieldBySomeMethod(excelImportEntity.getMethods(), object);}url = object.getClass().getName().split("\\.")[object.getClass().getName().split("\\.").length - 1];return excelImportEntity.getSaveUrl() + File.separator + url;}return excelImportEntity.getSaveUrl();}private <T> List<T> importExcel(Collection<T> result, Sheet sheet, Class<?> pojoClass,ImportParams params,Map<String, PictureData> pictures) throws Exception {List collection = new ArrayList();Map<String, ExcelImportEntity> excelParams = new HashMap<>();List<ExcelCollectionParams> excelCollection = new ArrayList<>();String targetId = null;i18nHandler = params.getI18nHandler();boolean isMap = Map.class.equals(pojoClass);if (!isMap) {Field[] fileds = PoiPublicUtil.getClassFields(pojoClass);ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);if (etarget != null) {targetId = etarget.value();}getAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null, null);}Iterator<Row> rows = sheet.rowIterator();for (int j = 0; j < params.getTitleRows(); j++) {rows.next();}Map<Integer, String> titlemap = getTitleMap(rows, params, excelCollection, excelParams);checkIsValidTemplate(titlemap, excelParams, params, excelCollection);Row row = null;Object object = null;String picId;int readRow = 1;//跳过无效行for (int i = 0; i < params.getStartRows(); i++) {rows.next();}//判断index 和集合,集合情况默认为第一列if (excelCollection.size() > 0 && params.getKeyIndex() == null) {params.setKeyIndex(0);}int endRow = sheet.getLastRowNum() - params.getLastOfInvalidRow();if (params.getReadRows() > 0) {endRow = Math.min(params.getReadRows(), endRow);}if (params.isConcurrentTask()) {ForkJoinPool forkJoinPool = new ForkJoinPool();ExcelImportForkJoinWork task = new ExcelImportForkJoinWork(params.getStartRows() + params.getHeadRows() + params.getTitleRows(), endRow, sheet, params, pojoClass, this, targetId, titlemap, excelParams);ExcelImportResult forkJoinResult = forkJoinPool.invoke(task);collection = forkJoinResult.getList();failCollection = forkJoinResult.getFailList();} else {StringBuilder errorMsg;while (rows.hasNext()) {row = rows.next();// Fix 如果row为无效行时候跳出if (row.getRowNum() > endRow) {break;}/* 如果当前行的单元格都是无效的,那就继续下一行 */if (row.getLastCellNum()<0) {continue;}if(isMap && object != null) {((Map) object).put("excelRowNum", row.getRowNum());}errorMsg = new StringBuilder();// 判断是集合元素还是不是集合元素,如果是就继续加入这个集合,不是就创建新的对象// keyIndex 如果为空就不处理,仍然处理这一行if (params.getKeyIndex() != null&& (row.getCell(params.getKeyIndex()) == null|| StringUtils.isEmpty(getKeyValue(row.getCell(params.getKeyIndex()))))&& object != null) {for (ExcelCollectionParams param : excelCollection) {addListContinue(object, param, row, titlemap, targetId, pictures, params, errorMsg);}} else {object = PoiPublicUtil.createObject(pojoClass, targetId);try {Set<Integer> keys = titlemap.keySet();for (Integer cn : keys) {Cell cell = row.getCell(cn);String titleString = (String) titlemap.get(cn);if (excelParams.containsKey(titleString) || isMap) {if (excelParams.get(titleString) != null&& excelParams.get(titleString).getType() == BaseTypeConstants.IMAGE_TYPE) {picId = row.getRowNum() + "_" + cn;saveImage(object, picId, excelParams, titleString, pictures,params);} else {try {saveFieldValue(params, object, cell, excelParams, titleString, row);} catch (ExcelImportException e) {// 如果需要去校验就忽略,这个错误,继续执行if (params.isNeedVerify() && ExcelImportEnum.GET_VALUE_ERROR.equals(e.getType())) {errorMsg.append(" ").append(titleString).append(ExcelImportEnum.GET_VALUE_ERROR.getMsg());}}}}}//for (int i = row.getFirstCellNum(), le = titlemap.size(); i < le; i++) {//}if (object instanceof IExcelDataModel) {((IExcelDataModel) object).setRowNum(row.getRowNum());}for (ExcelCollectionParams param : excelCollection) {addListContinue(object, param, row, titlemap, targetId, pictures, params, errorMsg);}if (verifyingDataValidity(object, row, params, isMap, errorMsg)) {collection.add(object);} else {failCollection.add(object);}} catch (ExcelImportException e) {LOGGER.error("excel import error , row num:{},obj:{}", readRow, ReflectionToStringBuilder.toString(object));if (!e.getType().equals(ExcelImportEnum.VERIFY_ERROR)) {throw new ExcelImportException(e.getType(), e);}} catch (Exception e) {LOGGER.error("excel import error , row num:{},obj:{}", readRow, ReflectionToStringBuilder.toString(object));throw new RuntimeException(e);}}readRow++;}}return collection;}/*** 校验数据合法性*/public boolean verifyingDataValidity(Object object, Row row, ImportParams params,boolean isMap, StringBuilder fieldErrorMsg) {boolean isAdd = true;Cell cell = null;if (params.isNeedVerify()) {String errorMsg = PoiValidationUtil.validation(object, params.getVerifyGroup());if (StringUtils.isNotEmpty(errorMsg)) {cell = row.createCell(row.getLastCellNum());cell.setCellValue(errorMsg);if (object instanceof IExcelModel) {IExcelModel model = (IExcelModel) object;model.setErrorMsg(errorMsg);}isAdd = false;verifyFail = true;}}if (params.getVerifyHandler() != null) {ExcelVerifyHandlerResult result = params.getVerifyHandler().verifyHandler(object);if (!result.isSuccess()) {if (cell == null) {cell = row.createCell(row.getLastCellNum());}cell.setCellValue((StringUtils.isNoneBlank(cell.getStringCellValue())? cell.getStringCellValue() + "," : "") + result.getMsg());if (object instanceof IExcelModel) {IExcelModel model = (IExcelModel) object;model.setErrorMsg((StringUtils.isNoneBlank(model.getErrorMsg())? model.getErrorMsg() + "," : "") + result.getMsg());}isAdd = false;verifyFail = true;}}if ((params.isNeedVerify() || params.getVerifyHandler() != null) && fieldErrorMsg.length() > 0) {if (object instanceof IExcelModel) {IExcelModel model = (IExcelModel) object;model.setErrorMsg((StringUtils.isNoneBlank(model.getErrorMsg())? model.getErrorMsg() + "," : "") + fieldErrorMsg.toString());}if (cell == null) {cell = row.createCell(row.getLastCellNum());}cell.setCellValue((StringUtils.isNoneBlank(cell.getStringCellValue())? cell.getStringCellValue() + "," : "") + fieldErrorMsg.toString());isAdd = false;verifyFail = true;}if (cell != null) {cell.setCellStyle(errorCellStyle);failRow.add(row);if(isMap) {((Map) object).put("excelErrorMsg", cell.getStringCellValue());}} else {successRow.add(row);}return isAdd;}/*** 获取表格字段列名对应信息*/private Map<Integer, String> getTitleMap(Iterator<Row> rows, ImportParams params,List<ExcelCollectionParams> excelCollection,Map<String, ExcelImportEntity> excelParams) {Map<Integer, String> titlemap = new LinkedHashMap<Integer, String>();Iterator<Cell> cellTitle;String collectionName = null;ExcelCollectionParams collectionParams = null;Row row = null;for (int j = 0; j < params.getHeadRows(); j++) {row = rows.next();if (row == null) {continue;}cellTitle = row.cellIterator();while (cellTitle.hasNext()) {Cell cell = cellTitle.next();String value = getKeyValue(cell);value = value.replace("\n", "");int i = cell.getColumnIndex();//用以支持重名导入if (StringUtils.isNotEmpty(value)) {if (titlemap.containsKey(i)) {collectionName = titlemap.get(i);collectionParams = getCollectionParams(excelCollection, collectionName);titlemap.put(i, collectionName + "_" + value);} else if (StringUtils.isNotEmpty(collectionName) && collectionParams != null&& collectionParams.getExcelParams().containsKey(collectionName + "_" + value)) {titlemap.put(i, collectionName + "_" + value);} else {collectionName = null;collectionParams = null;}if (StringUtils.isEmpty(collectionName)) {titlemap.put(i, value);}}}}// 处理指定列的情况Set<String> keys = excelParams.keySet();for (String key : keys) {if (key.startsWith("FIXED_")) {String[] arr = key.split("_");titlemap.put(Integer.parseInt(arr[1]), key);}}return titlemap;}/*** 获取这个名称对应的集合信息*/private ExcelCollectionParams getCollectionParams(List<ExcelCollectionParams> excelCollection,String collectionName) {for (ExcelCollectionParams excelCollectionParams : excelCollection) {if (collectionName.equals(excelCollectionParams.getExcelName())) {return excelCollectionParams;}}return null;}/*** Excel 导入 field 字段类型 Integer,Long,Double,Date,String,Boolean*/public ExcelImportResult importExcelByIs(InputStream inputstream, Class<?> pojoClass,ImportParams params, boolean needMore) throws Exception {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Excel import start ,class is {}", pojoClass);}List<T> result = new ArrayList<T>();ByteArrayOutputStream baos = new ByteArrayOutputStream();ExcelImportResult importResult;try {byte[] buffer = new byte[1024];int len;while ((len = inputstream.read(buffer)) > -1) {baos.write(buffer, 0, len);}baos.flush();InputStream userIs = new ByteArrayInputStream(baos.toByteArray());if (LOGGER.isDebugEnabled()) {LOGGER.debug("Excel clone success");}Workbook book = WorkbookFactory.create(userIs);boolean isXSSFWorkbook = !(book instanceof HSSFWorkbook);if (LOGGER.isDebugEnabled()) {LOGGER.debug("Workbook create success");}importResult = new ExcelImportResult();createErrorCellStyle(book);Map<String, PictureData> pictures;for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()+ params.getSheetNum(); i++) {if (LOGGER.isDebugEnabled()) {LOGGER.debug(" start to read excel by is ,startTime is {}", new Date());}if (isXSSFWorkbook) {pictures = PoiPublicUtil.getSheetPictrues07((XSSFSheet) book.getSheetAt(i),(XSSFWorkbook) book);} else {pictures = PoiPublicUtil.getSheetPictrues03((HSSFSheet) book.getSheetAt(i),(HSSFWorkbook) book);}if (LOGGER.isDebugEnabled()) {LOGGER.debug(" end to read excel by is ,endTime is {}", new Date());}result.addAll(importExcel(result, book.getSheetAt(i), pojoClass, params, pictures));if (LOGGER.isDebugEnabled()) {LOGGER.debug(" end to read excel list by sheet ,endTime is {}", new Date());}if (params.isReadSingleCell()) {readSingleCell(importResult, book.getSheetAt(i), params);if (LOGGER.isDebugEnabled()) {LOGGER.debug(" read Key-Value ,endTime is {}", System.currentTimeMillis());}}}if (params.isNeedSave()) {saveThisExcel(params, pojoClass, isXSSFWorkbook, book);}importResult.setList(result);if (needMore) {InputStream successIs = new ByteArrayInputStream(baos.toByteArray());try {Workbook successBook = WorkbookFactory.create(successIs);if (params.isVerifyFileSplit()){importResult.setWorkbook(removeSuperfluousRows(successBook, failRow, params));importResult.setFailWorkbook(removeSuperfluousRows(book, successRow, params));} else {importResult.setWorkbook(book);}importResult.setFailList(failCollection);importResult.setVerifyFail(verifyFail);} finally {successIs.close();}}} finally {IOUtils.closeQuietly(baos);}return importResult;}private Workbook removeSuperfluousRows(Workbook book, List<Row> rowList, ImportParams params) {for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()+ params.getSheetNum(); i++) {for (int j = rowList.size() - 1; j >= 0; j--) {if (rowList.get(j).getRowNum() < rowList.get(j).getSheet().getLastRowNum()) {book.getSheetAt(i).shiftRows(rowList.get(j).getRowNum() + 1, rowList.get(j).getSheet().getLastRowNum(), 1);} else if (rowList.get(j).getRowNum() == rowList.get(j).getSheet().getLastRowNum()) {book.getSheetAt(i).createRow(rowList.get(j).getRowNum() + 1);book.getSheetAt(i).shiftRows(rowList.get(j).getRowNum() + 1, rowList.get(j).getSheet().getLastRowNum() + 1, 1);}}}return book;}/*** 按照键值对的方式取得Excel里面的数据*/private void readSingleCell(ExcelImportResult result, Sheet sheet, ImportParams params) {if (result.getMap() == null) {result.setMap(new HashMap<String, Object>());}for (int i = 0; i < params.getTitleRows() + params.getHeadRows() + params.getStartRows(); i++) {getSingleCellValueForRow(result, sheet.getRow(i), params);}for (int i = sheet.getLastRowNum() - params.getLastOfInvalidRow(); i < sheet.getLastRowNum(); i++) {getSingleCellValueForRow(result, sheet.getRow(i), params);}}private void getSingleCellValueForRow(ExcelImportResult result, Row row, ImportParams params) {for (int j = row.getFirstCellNum(), le = row.getLastCellNum(); j < le; j++) {String text = PoiCellUtil.getCellValue(row.getCell(j));if (StringUtils.isNoneBlank(text) && text.endsWith(params.getKeyMark())) {if (result.getMap().containsKey(text)) {if (result.getMap().get(text) instanceof String) {List<String> list = new ArrayList<String>();list.add((String) result.getMap().get(text));result.getMap().put(text, list);}((List) result.getMap().get(text)).add(PoiCellUtil.getCellValue(row.getCell(++j)));} else {result.getMap().put(text, PoiCellUtil.getCellValue(row.getCell(++j)));}}}}/*** 检查是不是合法的模板*/private void checkIsValidTemplate(Map<Integer, String> titlemap,Map<String, ExcelImportEntity> excelParams,ImportParams params,List<ExcelCollectionParams> excelCollection) {if (params.getImportFields() != null) {// 同时校验列顺序if (params.isNeedCheckOrder()) {if (params.getImportFields().length != titlemap.size()) {LOGGER.error("excel列顺序不一致");throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}int i = 0;for (String title : titlemap.values()) {if (!StringUtils.equals(title, params.getImportFields()[i++])) {LOGGER.error("excel列顺序不一致");throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}}} else {for (int i = 0, le = params.getImportFields().length; i < le; i++) {if (!titlemap.containsValue(params.getImportFields()[i])) {throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}}}} else {Collection<ExcelImportEntity> collection = excelParams.values();for (ExcelImportEntity excelImportEntity : collection) {if (excelImportEntity.isImportField()&& !titlemap.containsValue(excelImportEntity.getName())) {LOGGER.error(excelImportEntity.getName() + "必须有,但是没找到");throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}}for (int i = 0, le = excelCollection.size(); i < le; i++) {ExcelCollectionParams collectionparams = excelCollection.get(i);collection = collectionparams.getExcelParams().values();for (ExcelImportEntity excelImportEntity : collection) {if (excelImportEntity.isImportField() && !titlemap.containsValue(collectionparams.getExcelName() + "_" + excelImportEntity.getName())) {throw new ExcelImportException(ExcelImportEnum.IS_NOT_A_VALID_TEMPLATE);}}}}}/*** 保存字段值(获取值,校验值,追加错误信息)*/public void saveFieldValue(ImportParams params, Object object, Cell cell,Map<String, ExcelImportEntity> excelParams, String titleString,Row row) throws Exception {Object value = cellValueServer.getValue(params.getDataHandler(), object, cell, excelParams,titleString, params.getDictHandler());if (object instanceof Map) {if (params.getDataHandler() != null) {params.getDataHandler().setMapValue((Map) object, titleString, value);} else {((Map) object).put(titleString, value);}} else {setValues(excelParams.get(titleString), object, value);}}/*** @param object* @param picId* @param excelParams* @param titleString* @param pictures* @param params* @throws Exception*/private void saveImage(Object object, String picId, Map<String, ExcelImportEntity> excelParams,String titleString, Map<String, PictureData> pictures,ImportParams params) throws Exception {if (pictures == null) {return;}PictureData image = pictures.get(picId);if (image == null) {return;}byte[] data = image.getData();String fileName = "pic" + Math.round(Math.random() * 100000000000L);fileName += "." + PoiPublicUtil.getFileExtendName(data);if (excelParams.get(titleString).getSaveType() == 1) {String path = getSaveUrl(excelParams.get(titleString), object);File savefile = new File(path);if (!savefile.exists()) {savefile.mkdirs();}savefile = new File(path + File.separator + fileName);FileOutputStream fos = new FileOutputStream(savefile);try {fos.write(data);} finally {IOUtils.closeQuietly(fos);}setValues(excelParams.get(titleString), object,getSaveUrl(excelParams.get(titleString), object) + File.separator + fileName);} else {setValues(excelParams.get(titleString), object, data);}}private void createErrorCellStyle(Workbook workbook) {errorCellStyle = workbook.createCellStyle();Font font = workbook.createFont();font.setColor(Font.COLOR_RED);errorCellStyle.setFont(font);}}
3.3 Maven 依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.4.0</version></dependency><!-- 建议只用start --><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.4.0</version></dependency><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-web</artifactId><version>4.4.0</version></dependency><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-annotation</artifactId><version>4.4.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>5.2.1.Final</version></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator-annotation-processor</artifactId><version>5.2.1.Final</version></dependency><dependency><groupId>org.glassfish.web</groupId><artifactId>javax.el</artifactId><version>2.2.4</version></dependency>
4.git 完整代码
防止复现不了我说的情况,git
相关文章:

【easypoi 一对多导入解决方案】
easypoi 一对多导入解决方案 1.需求2.复现问题2.1校验时获取不到一对多中多的完整数据2.2控制台报错 Cannot add merged region B5:B7 to sheet because it overlaps with an existing merged region (B3:B5). 3.如何解决第二个问题处理: Cannot add merged region …...

DDOS攻击会对网站服务器造成哪些影响?
DDOS攻击作为日常生活正比较常见的网络攻击类型,可以让多台计算机在同一时间内遭受到攻击,下面小编就带领大家一起来了解一下DDOS攻击会对网站服务器造成哪些影响吧! 首先DDOS攻击在进行攻击的过程中,可以对源IP地址进行伪造&…...

linux基础指令的认识
在正式学习linux前,可以简单认识一下linux与win的区别 win:是图形界面,用户操作更简单;在刚开始win也是黑屏终端 指令操作,图形界面就是历史发展的结果。Linux:也存在图形界面比如desktop OS;但…...

html5 + css3(下)
目录 CSS基础基础认识体验cssCSS引入方式 基础选择器选择器-标签选择器-类选择器-id选择器-通配符 字体和文本样式1.1 字体大小1.2 字体粗细1.3 字体样式(是否倾斜)1.4 常见字体系列(了解)1.5 字体系列拓展-层叠性font复合属性文本…...

828华为云征文|部署个人文档管理系统 Docspell
828华为云征文|部署个人文档管理系统 Docspell 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 Docspell3.1 Docspell 介绍3.2 Docspell 部署3.3 Docspell 使用…...

【深度学习】—激活函数、ReLU 函数、 Sigmoid 函数、Tanh 函数
【深度学习】—激活函数、ReLU 函数、 Sigmoid 函数、Tanh 函数 4.1.2 激活函数ReLU 函数参数化 ReLU Sigmoid 函数背景绘制 sigmoid 函数Sigmoid 函数的导数 Tanh 函数Tanh 函数的导数总结 4.1.2 激活函数 激活函数(activation function)用于计算加权和…...

对于基础汇编的趣味认识
汇编语言 机器指令 机器语言是机器指令的集合 机器指令展开来讲就是一台机器可以正确执行的命令 电子计算机的机器指令是一列二进制数字 (计算机将其转变为一列高低电平,使得计算机的电子器件受到驱动,进行运算 寄存器:微处理器…...

网络基础知识笔记(一)
什么是计算机网络 1.计算机网络发展的第一个阶段:(60年代) 标志性事件:ARPANET 关键技术:分组交换 计算机网络发展的第二个阶段:(70-80年代) 标志性事件:NSFNET 关键技术:TCP/IP 计算机网络发展的第三个阶段ÿ…...

fatal: urdf 中的 CRLF 将被 LF 替换
git add relaxed_ik_ros2 fatal: relaxed_ik_ros2/relaxed_ik_core/configs/urdfs/mobile_spot_arm.urdf 中的 CRLF 将被 LF 替换 这个错误信息表示 Git 在处理文件 mobile_spot_arm.urdf 时发现它使用了 CRLF(回车换行符,常见于 Windows 系统࿰…...

构建electron项目
1. 使用electron-vite构建工具 官网链接 安装构建工具 pnpm i electron-vite -g创建electron-vite项目 pnpm create quick-start/electron安装所有依赖 pnpm i其他 pnpm -D add sass scss1. 启动项目 2. 配置 package.json "dev": "electron-vite dev --…...

Stable Diffusion绘画 | 插件-Deforum:动态视频生成(中篇)
本篇文章重点讲解参数最多的 关键帧 模块。 「动画模式」选择「3D」: 下方「运动」Tab 会有一系列参数: 以下4个参数,只有「动画模式」选择「2D」才会生效,可忽略: 运动 平移 X 让镜头左右移动: 大于0&a…...

STM32中断——外部中断
目录 一、概述 二、外部中断(Extern Interrupt简称EXTI) 三、实例-对射式红外传感器 1、配置中断: 2 、完整代码 一、概述 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当…...

LeetCode78 子集
题目: 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的 子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1: 输入:nums [1,2,3] 输出:[[…...

《python语言程序设计》2018版第8章19题几何Rectangle2D类(下)-头疼的几何和数学
希望这个下集里能有完整的代码 一、containsPoint实现 先从网上找一下Statement expected, found Py:DEDENTTAB还是空格呢??小小总结如何拆分矩形的四个点呢.我们来小小的测试一下这个函数结果出在哪里呢???修改完成variable in function should be lowercase 函数变量应该…...

【C++】入门基础介绍(上)C++的发展历史与命名空间
文章目录 1. 前言2. C发展历史2. 1 C版本更新特性一览2. 2 关于C23的一个小故事: 3. C的重要性3. 1 编程语言排行榜3. 2 C在工作领域中的应用 4. C学习建议和书籍推荐4. 1 C学习难度4. 2 学习书籍推荐 5. C的第一个程序6. 命名空间6. 1 namespace的价值6. 2 namespace的定义6. …...

dll动态库加载失败导致程序启动报错以及dll库加载失败的常见原因分析与总结
目录 1、问题说明 2、dll库的隐式加载与动态加载 2.1、dll库的隐式加载 2.2、dll库的显式加载 3、使用Process Explorer查看进程加载的dll库信息以及动态加载的dll库有没有加载成功 3.1、使用Process Explorer查看进程加载的dll库信息 3.2、使用Process Explorer查看动态…...

SAP MM学习笔记 - 豆知识10 - OMSY 初期化会计期间,ABAP调用MMPV/MMRV来批量更新会计期间(TODO)
之前用MMRV,MMPV来一次一个月来修改会计期间。 如果是老的测试机,可能是10几年前的,一次1个月,更新到当前期间,搞个100多次,手都抖。 SAP MM学习笔记 - 错误 M7053 - Posting only possible in periods 2…...

Pytorch实现RNN实验
一、实验要求 用 Pytorch 模块的 RNN 实现生成唐诗。要求给定一个字能够生成一首唐诗。 二、实验目的 理解循环神经网络(RNN)的基本原理:通过构建一个基于RNN的诗歌生成模型,学会RNN是如何处理序列数据的,以及如何在…...

四、Drf认证组件
四、Drf认证组件 4.1 快速使用 from django.shortcuts import render,HttpResponse from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.authentication import BaseAuthentication from rest_framework.exception…...

C++:静态成员
静态成员涉及到的关键字尾static 静态成员变量要在类外初始化 去掉static关键字类型类名::变量名 静态成员变量不属于任何对象 所有对象共享一份 静态成员可以不通过对象直接访问 类名::成员名 静态成员依旧受访问修饰符的约束 …...

28 Vue3之搭建公司级项目规范
可以看到保存的时候ref这行被提到了最前面的一行 要求内置库放在组件的前面称为auto fix,数组new arry改成了字面量,这就是我们配置的规范 js规范使用的是airbnb规范模块使用的是antfu 组合prettier&eslint airbnb规范: https://github…...

【pytorch】张量求导3
再接上文,补一下作者未补完的矩阵运算的坑。 首先贴一下原作者的图,将其转化为如下代码: import torch import torch.nn as nn import torch.optim as optim# 定义一个简单的两层神经网络 class TwoLayerNet(nn.Module):def __init__(self):super(TwoLayerNet, self).__in…...

Servlet——springMvc底层原理
我们也先了解一下什么的动态资源,什么是静态资源。 静态资源:无需程序运行就可以获取的资源(照片、html、css、js等) 动态资源:需要通关程序运行才可以获得的资源。 (其实动态、静态的资源都与Servlet有…...

Json 在线可视化工具,分享几个
文章目录 1.json.cn2.json4u.cn3.jsonvisual.com4.jsoncrack5.altearius.github.io6.json.wanvb.com 前序:本文是对多种 Json 在线可视化工具 的介绍、分享。Json官网 https://www.json.org/json-en.html 个人比较中意第四款: https://jsoncrack.com/ed…...

LLM | llama.cpp 安装使用(支持CPU、Metal及CUDA的单卡/多卡推理)
1. 详细步骤 1.1 安装 cuda 等 nvidia 依赖(非CUDA环境运行可跳过) # 以 CUDA Toolkit 12.4: Ubuntu-22.04/24.04(x86_64) 为例,注意区分 WSL 和 Ubuntu,详见 https://developer.nvidia.com/cuda-12-4-1-download-archive?targ…...

矩阵求解复数(aniwoth求解串扰)
所以这种求解串扰的格式是因为,有串扰的共轭项在方程组中 复数共轭项的作用,但是这是二次方程,...

Redis: Sentinel哨兵监控架构及环境搭建
概述 在主从模式下,我们通过从节点只读模式提高了系统的并发能力并发不断增加,只需要扩展从节点即可,只要主从服务器之间,网络连接正常主服务器就会将写入自己的数据同步更新给从服务器,从而保证主从服务器的数据相同…...

C++ 语言特性30 - 模板介绍
目录 一:C11 之前的模板特性 1. 函数模板: 2. 类模板: 3. 模板特化: 4. 模板参数: 5. 模板元编程: 二:C11的模板特性 1. 变长模板(Variadic Templates)ÿ…...

算法笔记(七)——哈希表
文章目录 两数之和判定是否互为字符重排存在重复元素存在重复元素 II字母异位词分组 哈希表:一种存储数据的容器; 可以快速查找某个元素,时间复杂度O(1); 当频繁查找某一个数时,我们可以使用哈希表 创建一个容器&#…...

【基础算法总结】链表篇
目录 一, 链表常用技巧和操作总结二,算法原理和代码实现2.两数相加24.两两交换链表中的节点143.重排链表23.合并k个升序链表25.k个一组翻转链表 三,算法总结 一, 链表常用技巧和操作总结 有关链表的算法题也是一类常见并且经典的题…...