【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关键字类型类名::变量名 静态成员变量不属于任何对象 所有对象共享一份 静态成员可以不通过对象直接访问 类名::成员名 静态成员依旧受访问修饰符的约束 …...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...