diff --git a/autopoi/autopoi-web/pom.xml b/autopoi/autopoi-web/pom.xml
new file mode 100644
index 0000000..a2aed2e
--- /dev/null
+++ b/autopoi/autopoi-web/pom.xml
@@ -0,0 +1,43 @@
+
+ 4.0.0
+
+ org.jeecgframework.boot3
+ autopoi-parent
+ 3.7.0
+
+ autopoi-web
+
+
+
+ org.jeecgframework.boot3
+ autopoi
+ ${autopoi.version}
+
+
+
+
+ org.springframework
+ spring-webmvc
+ true
+
+
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ 6.0.0
+ provided
+ true
+
+
+
+ org.apache.logging.log4j
+ log4j-to-slf4j
+ 2.11.2
+ true
+ test
+
+
+
\ No newline at end of file
diff --git a/autopoi/autopoi/pom.xml b/autopoi/autopoi/pom.xml
new file mode 100644
index 0000000..76f5581
--- /dev/null
+++ b/autopoi/autopoi/pom.xml
@@ -0,0 +1,73 @@
+
+ 4.0.0
+
+ org.jeecgframework.boot3
+ autopoi-parent
+ 3.7.0
+
+ autopoi
+
+
+
+ org.apache.poi
+ poi
+
+
+ org.apache.poi
+ poi-ooxml
+
+
+ xml-apis
+ xml-apis
+
+
+
+
+
+ org.apache.poi
+ poi-ooxml-full
+
+
+
+
+
+
+
+ xerces
+ xercesImpl
+
+
+
+ org.apache.poi
+ poi-scratchpad
+
+
+
+
+ com.google.guava
+ guava
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+
+ org.slf4j
+ slf4j-api
+ true
+
+
+
+
+ org.springframework
+ spring-webmvc
+ true
+
+
+
+
\ No newline at end of file
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelImportCheckUtil.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelImportCheckUtil.java
new file mode 100644
index 0000000..6f13dab
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelImportCheckUtil.java
@@ -0,0 +1,457 @@
+package org.jeecgframework.poi.excel;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.jeecgframework.core.util.ApplicationContextUtil;
+import org.jeecgframework.dict.service.AutoPoiDictServiceI;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.jeecgframework.poi.excel.annotation.ExcelCollection;
+import org.jeecgframework.poi.excel.annotation.ExcelTarget;
+import org.jeecgframework.poi.excel.annotation.ExcelVerify;
+import org.jeecgframework.poi.excel.entity.ImportParams;
+import org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams;
+import org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;
+import org.jeecgframework.poi.excel.entity.params.ExcelVerifyEntity;
+import org.jeecgframework.poi.excel.imports.ExcelImportServer;
+import org.jeecgframework.poi.exception.excel.ExcelImportException;
+import org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum;
+import org.jeecgframework.poi.util.ExcelUtil;
+import org.jeecgframework.poi.util.PoiPublicUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * EXCEL INCLUE CHECK
+ * 验证excel标题是否存在,当前默认有0.8(80%)即可通过验证
+ */
+public class ExcelImportCheckUtil {
+ private final static Logger LOGGER = LoggerFactory.getLogger(ExcelImportCheckUtil.class);
+
+ /**当有标题到达多少可以通过验证*/
+ public static final Double defScreenRate = 0.8;
+
+ /**
+ * check inclue filed match rate
+ * @param inputstream
+ * @param pojoClass
+ * @param params
+ * @return
+ */
+ public static Boolean check(InputStream inputstream, Class> pojoClass, ImportParams params) {
+ return check(inputstream,pojoClass,params,defScreenRate);
+ }
+ /**
+ * check inclue filed match rate
+ * @param inputstream
+ * @param pojoClass
+ * @param params
+ * @param screenRate field match rate (defalut:0.8)
+ * @return
+ */
+ public static Boolean check(InputStream inputstream, Class> pojoClass, ImportParams params, Double screenRate) {
+ Workbook book = null;
+ int errorNum = 0;
+ int successNum = 0;
+ if (!(inputstream.markSupported())) {
+ inputstream = new PushbackInputStream(inputstream, 8);
+ }
+ try {
+// if (POIFSFileSystem.hasPOIFSHeader(inputstream)) {
+// book = new HSSFWorkbook(inputstream);
+// } else if (POIXMLDocument.hasOOXMLHeader(inputstream)) {
+// book = new XSSFWorkbook(OPCPackage.open(inputstream));
+// }
+ book = WorkbookFactory.create(inputstream);
+ LOGGER.info(" >>> poi3升级到4兼容改造工作, pojoClass="+pojoClass);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ for (int i = 0; i < params.getSheetNum(); i++) {
+ Row row = null;
+ //跳过表头和标题行
+ Iterator rows;
+ try{
+ rows= book.getSheetAt(i).rowIterator();
+ }catch (Exception e){
+ //为空说明读取不到,故不是excel
+ throw new RuntimeException("请导入正确格式的excel文件!");
+ }
+
+
+ for (int j = 0; j < params.getTitleRows() + params.getHeadRows(); j++) {
+ try{
+ row = rows.next();
+ }catch (NoSuchElementException e){
+ //为空说明标题不出在,excel格式错误
+ throw new RuntimeException("请填写内容标题!");
+ }
+ }
+ Sheet sheet = book.getSheetAt(i);
+ Map titlemap = null;
+ try {
+ titlemap = getTitleMap(sheet, params);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ Set columnIndexSet = titlemap.keySet();
+ Integer maxColumnIndex = Collections.max(columnIndexSet);
+ Integer minColumnIndex = Collections.min(columnIndexSet);
+ while (rows.hasNext() && (row == null || sheet.getLastRowNum() - row.getRowNum() > params.getLastOfInvalidRow())) {
+ row = rows.next();
+ Map excelParams = new HashMap();
+ List excelCollection = new ArrayList();
+ String targetId = null;
+ if (!Map.class.equals(pojoClass)) {
+ Field fileds[] = PoiPublicUtil.getClassFields(pojoClass);
+ ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);
+ if (etarget != null) {
+ targetId = etarget.value();
+ }
+ try {
+ getAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ int firstCellNum = row.getFirstCellNum();
+ if (firstCellNum > minColumnIndex) {
+ firstCellNum = minColumnIndex;
+ }
+ int lastCellNum = row.getLastCellNum();
+ if (lastCellNum < maxColumnIndex + 1) {
+ lastCellNum = maxColumnIndex + 1;
+ }
+ for (int j = firstCellNum, le = lastCellNum; j < le; j++) {
+ String titleString = (String) titlemap.get(j);
+ if (excelParams.containsKey(titleString) || Map.class.equals(pojoClass)) {
+ successNum+=1;
+ }else{
+ if(excelCollection.size()>0){
+ Iterator var33 = excelCollection.iterator();
+ ExcelCollectionParams param = (ExcelCollectionParams)var33.next();
+ if (param.getExcelParams().containsKey(titleString)) {
+ successNum+=1;
+ }else{
+ errorNum+=1;
+ }
+ }else{
+ errorNum+=1;
+ }
+ }
+ }
+ if(successNumerrorNum){
+ if(errorNum>0){
+ double newNumber = (double) successNum / (successNum + errorNum);
+ BigDecimal bg = new BigDecimal(newNumber);
+ double f1 = bg.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
+ if(f1 getTitleMap(Sheet sheet, ImportParams params) throws Exception {
+ Map titlemap = new HashMap();
+ Iterator cellTitle = null;
+ String collectionName = null;
+ Row headRow = null;
+ int headBegin = params.getTitleRows();
+ int allRowNum = sheet.getPhysicalNumberOfRows();
+ while(headRow == null && headBegin < allRowNum){
+ headRow = sheet.getRow(headBegin++);
+ }
+ if(headRow==null){
+ throw new Exception("不识别该文件");
+ }
+ if (ExcelUtil.isMergedRegion(sheet, headRow.getRowNum(), 0)) {
+ params.setHeadRows(2);
+ }else{
+ params.setHeadRows(1);
+ }
+ cellTitle = headRow.cellIterator();
+ while (cellTitle.hasNext()) {
+ Cell cell = cellTitle.next();
+ String value = getKeyValue(cell);
+ if (StringUtils.isNotEmpty(value)) {
+ titlemap.put(cell.getColumnIndex(), value);//加入表头列表
+ }
+ }
+
+ //多行表头
+ for (int j = headBegin; j < headBegin + params.getHeadRows()-1; j++) {
+ headRow = sheet.getRow(j);
+ cellTitle = headRow.cellIterator();
+ while (cellTitle.hasNext()) {
+ Cell cell = cellTitle.next();
+ String value = getKeyValue(cell);
+ if (StringUtils.isNotEmpty(value)) {
+ int columnIndex = cell.getColumnIndex();
+ //当前cell的上一行是否为合并单元格
+ if(ExcelUtil.isMergedRegion(sheet, cell.getRowIndex()-1, columnIndex)){
+ collectionName = ExcelUtil.getMergedRegionValue(sheet, cell.getRowIndex()-1, columnIndex);
+ if(params.isIgnoreHeader(collectionName)){
+ titlemap.put(cell.getColumnIndex(), value);
+ }else{
+ titlemap.put(cell.getColumnIndex(), collectionName + "_" + value);
+ }
+ }else{
+ titlemap.put(cell.getColumnIndex(), value);
+ }
+ }
+ }
+ }
+ return titlemap;
+ }
+ /**
+ * 获取key的值,针对不同类型获取不同的值
+ *
+ * @Author JEECG
+ * @date 20201023
+ * @param cell
+ * @return
+ */
+ private static String getKeyValue(Cell cell) {
+ if(cell==null){
+ return null;
+ }
+ Object obj = null;
+ switch (cell.getCellType()) {
+ case STRING:
+ obj = cell.getStringCellValue();
+ break;
+ case BOOLEAN:
+ obj = cell.getBooleanCellValue();
+ break;
+ case NUMERIC:
+ obj = cell.getNumericCellValue();
+ break;
+ case FORMULA:
+ obj = cell.getCellFormula();
+ break;
+ }
+ return obj == null ? null : obj.toString().trim();
+ }
+
+ /**
+ * 获取需要导出的全部字段
+ *
+ *
+ *
+ * @param targetId
+ * 目标ID
+ * @param fields
+ * @param excelCollection
+ * @throws Exception
+ */
+ public static void getAllExcelField(String targetId, Field[] fields, Map excelParams, List excelCollection, Class> pojoClass, List getMethods) throws Exception {
+ ExcelImportEntity excelEntity = null;
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ if (PoiPublicUtil.isNotUserExcelUserThis(null, field, targetId)) {
+ continue;
+ }
+ if (PoiPublicUtil.isCollection(field.getType())) {
+ // 集合对象设置属性
+ ExcelCollectionParams collection = new ExcelCollectionParams();
+ collection.setName(field.getName());
+ Map temp = new HashMap();
+ ParameterizedType pt = (ParameterizedType)field.getGenericType();
+ Class> clz = (Class)pt.getActualTypeArguments()[0];
+ collection.setType(clz);
+ getExcelFieldList(targetId, PoiPublicUtil.getClassFields(clz), clz, temp, (List)null);
+ collection.setExcelParams(temp);
+ collection.setExcelName(((ExcelCollection)field.getAnnotation(ExcelCollection.class)).name());
+ additionalCollectionName(collection);
+ excelCollection.add(collection);
+ } else if (PoiPublicUtil.isJavaClass(field)) {
+ addEntityToMap(targetId, field, (ExcelImportEntity)excelEntity, pojoClass, getMethods, excelParams);
+ } else {
+ List newMethods = new ArrayList();
+ if (getMethods != null) {
+ newMethods.addAll(getMethods);
+ }
+ newMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass));
+ getAllExcelField(targetId, PoiPublicUtil.getClassFields(field.getType()), excelParams, excelCollection, field.getType(), newMethods);
+ }
+ }
+ }
+ public static void getExcelFieldList(String targetId, Field[] fields, Class> pojoClass, Map temp, List getMethods) throws Exception {
+ ExcelImportEntity excelEntity = null;
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ if (!PoiPublicUtil.isNotUserExcelUserThis((List)null, field, targetId)) {
+ if (PoiPublicUtil.isJavaClass(field)) {
+ addEntityToMap(targetId, field, (ExcelImportEntity)excelEntity, pojoClass, getMethods, temp);
+ } else {
+ List newMethods = new ArrayList();
+ if (getMethods != null) {
+ newMethods.addAll(getMethods);
+ }
+
+ newMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass, field.getType()));
+ getExcelFieldList(targetId, PoiPublicUtil.getClassFields(field.getType()), field.getType(), temp, newMethods);
+ }
+ }
+ }
+ }
+ /**
+ * 追加集合名称到前面
+ *
+ * @param collection
+ */
+ private static void additionalCollectionName(ExcelCollectionParams collection) {
+ Set keys = new HashSet();
+ keys.addAll(collection.getExcelParams().keySet());
+ Iterator var3 = keys.iterator();
+
+ while(var3.hasNext()) {
+ String key = (String)var3.next();
+ collection.getExcelParams().put(collection.getExcelName() + "_" + key, collection.getExcelParams().get(key));
+ collection.getExcelParams().remove(key);
+ }
+ }
+ /**
+ * 把这个注解解析放到类型对象中
+ *
+ * @param targetId
+ * @param field
+ * @param excelEntity
+ * @param pojoClass
+ * @param getMethods
+ * @param temp
+ * @throws Exception
+ */
+ public static void addEntityToMap(String targetId, Field field, ExcelImportEntity excelEntity, Class> pojoClass, List getMethods, Map temp) throws Exception {
+ Excel excel = field.getAnnotation(Excel.class);
+ excelEntity = new ExcelImportEntity();
+ excelEntity.setType(excel.type());
+ excelEntity.setSaveUrl(excel.savePath());
+ excelEntity.setSaveType(excel.imageType());
+ excelEntity.setReplace(excel.replace());
+ excelEntity.setDatabaseFormat(excel.databaseFormat());
+ excelEntity.setVerify(getImportVerify(field));
+ excelEntity.setSuffix(excel.suffix());
+ excelEntity.setNumFormat(excel.numFormat());
+ excelEntity.setGroupName(excel.groupName());
+ //update-begin-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题
+ excelEntity.setMultiReplace(excel.multiReplace());
+ if(StringUtils.isNotEmpty(excel.dicCode())){
+ AutoPoiDictServiceI jeecgDictService = null;
+ try {
+ jeecgDictService = ApplicationContextUtil.getContext().getBean(AutoPoiDictServiceI.class);
+ } catch (Exception e) {
+ }
+ if(jeecgDictService!=null){
+ String[] dictReplace = jeecgDictService.queryDict(excel.dictTable(), excel.dicCode(), excel.dicText());
+ if(excelEntity.getReplace()!=null && dictReplace!=null && dictReplace.length!=0){
+ excelEntity.setReplace(dictReplace);
+ }
+ }
+ }
+ //update-end-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题
+ getExcelField(targetId, field, excelEntity, excel, pojoClass);
+ if (getMethods != null) {
+ List newMethods = new ArrayList();
+ newMethods.addAll(getMethods);
+ newMethods.add(excelEntity.getMethod());
+ excelEntity.setMethods(newMethods);
+ }
+ temp.put(excelEntity.getName(), excelEntity);
+
+ }
+ public static void getExcelField(String targetId, Field field, ExcelImportEntity excelEntity, Excel excel, Class> pojoClass) throws Exception {
+ excelEntity.setName(getExcelName(excel.name(), targetId));
+ String fieldname = field.getName();
+ //update-begin-author:taoyan for:TASK #2798 【例子】导入扩展方法,支持自定义导入字段转换规则
+ excelEntity.setMethod(PoiPublicUtil.getMethod(fieldname, pojoClass, field.getType(),excel.importConvert()));
+ //update-end-author:taoyan for:TASK #2798 【例子】导入扩展方法,支持自定义导入字段转换规则
+ if (StringUtils.isNotEmpty(excel.importFormat())) {
+ excelEntity.setFormat(excel.importFormat());
+ } else {
+ excelEntity.setFormat(excel.format());
+ }
+ }
+ /**
+ * 判断在这个单元格显示的名称
+ *
+ * @param exportName
+ * @param targetId
+ * @return
+ */
+ public static String getExcelName(String exportName, String targetId) {
+ if (exportName.indexOf("_") < 0) {
+ return exportName;
+ }
+ String[] arr = exportName.split(",");
+ for (String str : arr) {
+ if (str.indexOf(targetId) != -1) {
+ return str.split("_")[0];
+ }
+ }
+ return null;
+ }
+ /**
+ * 获取导入校验参数
+ *
+ * @param field
+ * @return
+ */
+ public static ExcelVerifyEntity getImportVerify(Field field) {
+ ExcelVerify verify = field.getAnnotation(ExcelVerify.class);
+ if (verify != null) {
+ ExcelVerifyEntity entity = new ExcelVerifyEntity();
+ entity.setEmail(verify.isEmail());
+ entity.setInterHandler(verify.interHandler());
+ entity.setMaxLength(verify.maxLength());
+ entity.setMinLength(verify.minLength());
+ entity.setMobile(verify.isMobile());
+ entity.setNotNull(verify.notNull());
+ entity.setRegex(verify.regex());
+ entity.setRegexTip(verify.regexTip());
+ entity.setTel(verify.isTel());
+ return entity;
+ }
+ return null;
+ }
+}
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java
new file mode 100644
index 0000000..011c065
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java
@@ -0,0 +1,1026 @@
+/**
+ * Copyright 2013-2015 JEECG (jeecgos@163.com)
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 org.jeecgframework.poi.excel.export.template;
+
+import java.lang.reflect.Field;
+import java.util.*;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.jeecgframework.poi.cache.ExcelCache;
+import org.jeecgframework.poi.cache.ImageCache;
+import org.jeecgframework.poi.entity.ImageEntity;
+import org.jeecgframework.poi.excel.annotation.ExcelTarget;
+import org.jeecgframework.poi.excel.entity.TemplateExportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
+import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;
+import org.jeecgframework.poi.excel.entity.params.ExcelForEachParams;
+import org.jeecgframework.poi.excel.entity.params.ExcelTemplateParams;
+import org.jeecgframework.poi.excel.export.base.ExcelExportBase;
+import org.jeecgframework.poi.excel.export.styler.IExcelExportStyler;
+import org.jeecgframework.poi.excel.html.helper.MergedRegionHelper;
+import org.jeecgframework.poi.exception.excel.ExcelExportException;
+import org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum;
+
+import static org.jeecgframework.poi.util.PoiElUtil.*;
+
+import org.jeecgframework.poi.util.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Excel 导出根据模板导出
+ *
+ * @author JEECG
+ * @date 2013-10-17
+ * @version 1.0
+ */
+public final class ExcelExportOfTemplateUtil extends ExcelExportBase {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ExcelExportOfTemplateUtil.class);
+
+ /**
+ * 缓存TEMP 的for each创建的cell ,跳过这个cell的模板语法查找,提高效率
+ */
+ private Set tempCreateCellSet = new HashSet();
+ /**
+ * 模板参数,全局都用到
+ */
+ private TemplateExportParams teplateParams;
+ /**
+ * 单元格合并信息
+ */
+ private MergedRegionHelper mergedRegionHelper;
+
+
+ /**
+ * 往Sheet 填充正常数据,根据表头信息 使用导入的部分逻辑,坐对象映射
+ *
+ * @param teplateParams
+ * @param pojoClass
+ * @param dataSet
+ * @param workbook
+ */
+ private void addDataToSheet(Class> pojoClass, Collection> dataSet, Sheet sheet, Workbook workbook) throws Exception {
+
+ if (workbook instanceof XSSFWorkbook) {
+ super.type = ExcelType.XSSF;
+ }
+ // 获取表头数据
+ Map titlemap = getTitleMap(sheet);
+ Drawing patriarch = sheet.createDrawingPatriarch();
+ // 得到所有字段
+ Field[] fileds = PoiPublicUtil.getClassFields(pojoClass);
+ ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);
+ String targetId = null;
+ if (etarget != null) {
+ targetId = etarget.value();
+ }
+ // 获取实体对象的导出数据
+ List excelParams = new ArrayList();
+ getAllExcelField(null, targetId, fileds, excelParams, pojoClass, null);
+ // 根据表头进行筛选排序
+ sortAndFilterExportField(excelParams, titlemap);
+ short rowHeight = getRowHeight(excelParams);
+ int index = teplateParams.getHeadingRows() + teplateParams.getHeadingStartRow(), titleHeight = index;
+ // 下移数据,模拟插入
+ sheet.shiftRows(teplateParams.getHeadingRows() + teplateParams.getHeadingStartRow(), sheet.getLastRowNum(), getShiftRows(dataSet, excelParams), true, true);
+ if (excelParams.size() == 0) {
+ return;
+ }
+ Iterator> its = dataSet.iterator();
+ while (its.hasNext()) {
+ Object t = its.next();
+ index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight);
+ }
+ // 合并同类项
+ mergeCells(sheet, excelParams, titleHeight);
+ }
+
+ /**
+ * 下移数据
+ *
+ * @param its
+ * @param excelParams
+ * @return
+ */
+ private int getShiftRows(Collection> dataSet, List excelParams) throws Exception {
+ int size = 0;
+ Iterator> its = dataSet.iterator();
+ while (its.hasNext()) {
+ Object t = its.next();
+ size += getOneObjectSize(t, excelParams);
+ }
+ return size;
+ }
+
+ /**
+ * 获取单个对象的高度,主要是处理一堆多的情况
+ *
+ * @param styles
+ * @param rowHeight
+ * @throws Exception
+ */
+ public int getOneObjectSize(Object t, List excelParams) throws Exception {
+ ExcelExportEntity entity;
+ int maxHeight = 1;
+ for (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) {
+ entity = excelParams.get(k);
+ if (entity.getList() != null) {
+ Collection> list = (Collection>) entity.getMethod().invoke(t, new Object[] {});
+ if (list != null && list.size() > maxHeight) {
+ maxHeight = list.size();
+ }
+ }
+ }
+ return maxHeight;
+
+ }
+
+ public Workbook createExcleByTemplate(TemplateExportParams params, Class> pojoClass, Collection> dataSet, Map map) {
+ // step 1. 判断模板的地址
+ if (params == null || map == null || StringUtils.isEmpty(params.getTemplateUrl())) {
+ throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
+ }
+ Workbook wb = null;
+ // step 2. 判断模板的Excel类型,解析模板
+ try {
+ this.teplateParams = params;
+ wb = getCloneWorkBook();
+ // 创建表格样式
+ setExcelExportStyler((IExcelExportStyler) teplateParams.getStyle().getConstructor(Workbook.class).newInstance(wb));
+ // step 3. 解析模板
+ for (int i = 0, le = params.isScanAllsheet() ? wb.getNumberOfSheets() : params.getSheetNum().length; i < le; i++) {
+ if (params.getSheetName() != null && params.getSheetName().length > i && StringUtils.isNotEmpty(params.getSheetName()[i])) {
+ wb.setSheetName(i, params.getSheetName()[i]);
+ }
+ tempCreateCellSet.clear();
+ parseTemplate(wb.getSheetAt(i), map, params.isColForEach());
+ }
+ if (dataSet != null) {
+ // step 4. 正常的数据填充
+ dataHanlder = params.getDataHanlder();
+ if (dataHanlder != null) {
+ needHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());
+ }
+ addDataToSheet(pojoClass, dataSet, wb.getSheetAt(params.getDataSheetNum()), wb);
+ }
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ return null;
+ }
+ return wb;
+ }
+
+ /**
+ * 克隆excel防止操作原对象,workbook无法克隆,只能对excel进行克隆
+ *
+ * @param teplateParams
+ * @throws Exception
+ * @Author JEECG
+ * @date 2013-11-11
+ */
+ private Workbook getCloneWorkBook() throws Exception {
+ //update-begin-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505
+ return ExcelCache.getWorkbookByTemplate(teplateParams.getTemplateUrl(), teplateParams.getSheetNum(), teplateParams.isScanAllsheet());
+ //update-end-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505
+ }
+
+ /**
+ * 获取表头数据,设置表头的序号
+ *
+ * @param teplateParams
+ * @param sheet
+ * @return
+ */
+ private Map getTitleMap(Sheet sheet) {
+ Row row = null;
+ Iterator cellTitle;
+ Map titlemap = new HashMap();
+ for (int j = 0; j < teplateParams.getHeadingRows(); j++) {
+ row = sheet.getRow(j + teplateParams.getHeadingStartRow());
+ cellTitle = row.cellIterator();
+ int i = row.getFirstCellNum();
+ while (cellTitle.hasNext()) {
+ Cell cell = cellTitle.next();
+ String value = cell.getStringCellValue();
+ if (!StringUtils.isEmpty(value)) {
+ titlemap.put(value, i);
+ }
+ i = i + 1;
+ }
+ }
+ return titlemap;
+
+ }
+
+ private void parseTemplate(Sheet sheet, Map map, boolean colForeach) throws Exception {
+ deleteCell(sheet, map);
+ //update-begin-author:liusq---date:20220527--for: 模板导出列循环核心代码 ---
+ mergedRegionHelper = new MergedRegionHelper(sheet);
+ if (colForeach) {
+ colForeach(sheet, map);
+ }
+ //update-end-author:liusq---date:20220527--for: 模板导出列循环核心代码 ---
+ Row row = null;
+ int index = 0;
+ while (index <= sheet.getLastRowNum()) {
+ row = sheet.getRow(index++);
+ if (row == null) {
+ continue;
+ }
+ for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
+ if (row.getCell(i) != null && !tempCreateCellSet.contains(row.getRowNum() + "_" + row.getCell(i).getColumnIndex())) {
+ setValueForCellByMap(row.getCell(i), map);
+ }
+ }
+ }
+ }
+
+ /**
+ * 先判断删除,省得影响效率
+ *
+ * @param sheet
+ * @param map
+ * @throws Exception
+ */
+ private void deleteCell(Sheet sheet, Map map) throws Exception {
+ Row row = null;
+ Cell cell = null;
+ int index = 0;
+ while (index <= sheet.getLastRowNum()) {
+ row = sheet.getRow(index++);
+ if (row == null) {
+ continue;
+ }
+ for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
+ cell = row.getCell(i);
+ if (row.getCell(i) != null && (cell.getCellType() == CellType.STRING || cell.getCellType() == CellType.NUMERIC)) {
+ cell.setCellType(CellType.STRING);
+ String text = cell.getStringCellValue();
+ if (text.contains(IF_DELETE)) {
+ if (Boolean.valueOf(eval(text.substring(text.indexOf(START_STR) + 2, text.indexOf(END_STR)).trim(), map).toString())) {
+ PoiSheetUtility.deleteColumn(sheet, i);
+ }
+ cell.setCellValue("");
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 给每个Cell通过解析方式set值
+ *
+ * @param cell
+ * @param map
+ */
+ private void setValueForCellByMap(Cell cell, Map map) throws Exception {
+ CellType cellType = cell.getCellType();
+ if (cellType != CellType.STRING && cellType != CellType.NUMERIC) {
+ return;
+ }
+ String oldString;
+ cell.setCellType(CellType.STRING);
+ oldString = cell.getStringCellValue();
+ if (oldString != null && oldString.indexOf(START_STR) != -1 && !oldString.contains(FOREACH)) {
+ // step 2. 判断是否含有解析函数
+ String params = null;
+ boolean isNumber = false;
+ if (isNumber(oldString)) {
+ isNumber = true;
+ oldString = oldString.replace(NUMBER_SYMBOL, "");
+ }
+ while (oldString.indexOf(START_STR) != -1) {
+ params = oldString.substring(oldString.indexOf(START_STR) + 2, oldString.indexOf(END_STR));
+
+ oldString = oldString.replace(START_STR + params + END_STR, eval(params, map).toString());
+ }
+ // 如何是数值 类型,就按照数值类型进行设置
+ if (isNumber && StringUtils.isNotBlank(oldString)) {
+ cell.setCellValue(Double.parseDouble(oldString));
+ cell.setCellType(CellType.NUMERIC);
+ } else {
+ cell.setCellValue(oldString);
+ }
+ }
+ // 判断foreach 这种方法
+ if (oldString != null && oldString.contains(FOREACH)) {
+ addListDataToExcel(cell, map, oldString.trim());
+ }
+
+ }
+
+ private boolean isNumber(String text) {
+ return text.startsWith(NUMBER_SYMBOL) || text.contains("{" + NUMBER_SYMBOL) || text.contains(" " + NUMBER_SYMBOL);
+ }
+
+ /**
+ * 利用foreach循环输出数据
+ *
+ * @param cell
+ * @param map
+ * @param oldString
+ * @throws Exception
+ */
+ private void addListDataToExcel(Cell cell, Map map, String name) throws Exception {
+ boolean isCreate = !name.contains(FOREACH_NOT_CREATE);
+ boolean isShift = name.contains(FOREACH_AND_SHIFT);
+ name = name.replace(FOREACH_NOT_CREATE, EMPTY).replace(FOREACH_AND_SHIFT, EMPTY).replace(FOREACH, EMPTY).replace(START_STR, EMPTY);
+ String[] keys = name.replaceAll("\\s{1,}", " ").trim().split(" ");
+ Collection> datas = (Collection>) PoiPublicUtil.getParamsValue(keys[0], map);
+ //update-begin-author:liusq---date:20220609--for: [issues/3328]autopoi模板导出Excel功能,$fe: 遍历不好用 ---
+ Object[] columnsInfo = getAllDataColumns(cell, name.replace(keys[0], EMPTY),
+ mergedRegionHelper);
+ int rowspan = (Integer) columnsInfo[0], colspan = (Integer) columnsInfo[1];
+ @SuppressWarnings("unchecked")
+ List columns = (List) columnsInfo[2];
+ if (datas == null) {
+ return;
+ }
+ Iterator> its = datas.iterator();
+ Row row;
+ int rowIndex = cell.getRow().getRowNum() + 1;
+ //处理当前行
+ int loopSize = 0;
+ if (its.hasNext()) {
+ Object t = its.next();
+ cell.getRow().setHeight(columns.get(0).getHeight());
+ loopSize = setForeachRowCellValue(isCreate, cell.getRow(), cell.getColumnIndex(), t, columns, map,
+ rowspan, colspan, mergedRegionHelper)[0];
+ rowIndex += rowspan - 1 + loopSize - 1;
+ }
+ //修复不论后面有没有数据,都应该执行的是插入操作
+ if (isShift && datas.size() * rowspan > 1 && cell.getRowIndex() + rowspan <= cell.getRow().getSheet().getLastRowNum()) {
+ int lastRowNum = cell.getRow().getSheet().getLastRowNum();
+ int shiftRows = lastRowNum - cell.getRowIndex() - rowspan;
+ cell.getRow().getSheet().shiftRows(cell.getRowIndex() + rowspan, lastRowNum, (datas.size() - 1) * rowspan, true, true);
+ //update-begin-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格,第二个表格读取错误 ---
+ mergedRegionHelper.shiftRows(cell.getSheet(), cell.getRowIndex() + rowspan, (datas.size() - 1) * rowspan, shiftRows);
+ PoiExcelTempUtil.reset(cell.getSheet(), cell.getRowIndex() + rowspan + (datas.size() - 1) * rowspan, cell.getRow().getSheet().getLastRowNum());
+ //update-end-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格,第二个表格读取错误 ---
+ }
+ while (its.hasNext()) {
+ Object t = its.next();
+ row = createRow(rowIndex, cell.getSheet(), isCreate, rowspan);
+ row.setHeight(columns.get(0).getHeight());
+ loopSize = setForeachRowCellValue(isCreate, row, cell.getColumnIndex(), t, columns, map, rowspan,
+ colspan, mergedRegionHelper)[0];
+ rowIndex += rowspan + loopSize - 1;
+ }
+ //update-end-author:liusq---date:20220609--for: [issues/3328]autopoi模板导出Excel功能,$fe: 遍历不好用 ---
+ }
+
+ private void setForEeachCellValue(boolean isCreate, Row row, int columnIndex, Object t, List columns, Map map) throws Exception {
+ for (int i = 0, max = columnIndex + columns.size(); i < max; i++) {
+ if (row.getCell(i) == null)
+ row.createCell(i);
+ }
+ for (int i = 0, max = columns.size(); i < max; i++) {
+ boolean isNumber = false;
+ String tempStr = new String(columns.get(i).getName());
+ if (isNumber(tempStr)) {
+ isNumber = true;
+ tempStr = tempStr.replace(NUMBER_SYMBOL, "");
+ }
+ map.put(teplateParams.getTempParams(), t);
+ String val = eval(tempStr, map).toString();
+ if (isNumber && StringUtils.isNotEmpty(val)) {
+ row.getCell(i + columnIndex).setCellValue(Double.parseDouble(val));
+ row.getCell(i + columnIndex).setCellType(CellType.NUMERIC);
+ } else {
+ row.getCell(i + columnIndex).setCellValue(val);
+ }
+ row.getCell(i + columnIndex).setCellStyle(columns.get(i).getCellStyle());
+ tempCreateCellSet.add(row.getRowNum() + "_" + (i + columnIndex));
+ }
+
+ }
+
+ /**
+ * 获取迭代的数据的值
+ *
+ * @param cell
+ * @param name
+ * @return
+ */
+ private List getAllDataColumns(Cell cell, String name) {
+ List columns = new ArrayList();
+ cell.setCellValue("");
+ if (name.contains(END_STR)) {
+ columns.add(new ExcelTemplateParams(name.replace(END_STR, EMPTY).trim(), cell.getCellStyle(), cell.getRow().getHeight()));
+ return columns;
+ }
+ columns.add(new ExcelTemplateParams(name.trim(), cell.getCellStyle(), cell.getRow().getHeight()));
+ int index = cell.getColumnIndex();
+ //列数
+ int lastCellNum = cell.getRow().getLastCellNum();
+ Cell tempCell;
+ while (true) {
+ tempCell = cell.getRow().getCell(++index);
+ //--begin--date:2020/09/18---for:增加列数判断,防止提前跳出
+ if (tempCell == null&&index>=lastCellNum) {
+ break;
+ }
+ String cellStringString;
+ try {// 允许为空,单表示已经完结了,因为可能被删除了
+ cellStringString = tempCell.getStringCellValue();
+ if (StringUtils.isBlank(cellStringString)&&index>=lastCellNum) {
+ break;
+ }
+ } catch (Exception e) {
+ throw new ExcelExportException("for each 当中存在空字符串,请检查模板");
+ }
+ //--end--date:2020/09/18---for:增加列数判断,防止提前跳出
+ // 把读取过的cell 置为空
+ tempCell.setCellValue("");
+ if (cellStringString.contains(END_STR)) {
+ columns.add(new ExcelTemplateParams(cellStringString.trim().replace(END_STR, ""), tempCell.getCellStyle(), tempCell.getRow().getHeight()));
+ break;
+ } else {
+ if (cellStringString.trim().contains(teplateParams.getTempParams())) {
+ columns.add(new ExcelTemplateParams(cellStringString.trim(), tempCell.getCellStyle(), tempCell.getRow().getHeight()));
+ }else if(cellStringString.trim().equals(EMPTY)){
+ //可能是合并的单元格,允许空数据的设置
+ columns.add(new ExcelTemplateParams(EMPTY, tempCell.getCellStyle(), tempCell.getRow().getHeight()));
+ } else {
+ // 最后一行被删除了
+ break;
+ }
+ }
+
+ }
+ return columns;
+ }
+
+ /**
+ * 对导出序列进行排序和塞选
+ *
+ * @param excelParams
+ * @param titlemap
+ * @return
+ */
+ private void sortAndFilterExportField(List excelParams, Map titlemap) {
+ for (int i = excelParams.size() - 1; i >= 0; i--) {
+ if (excelParams.get(i).getList() != null && excelParams.get(i).getList().size() > 0) {
+ sortAndFilterExportField(excelParams.get(i).getList(), titlemap);
+ if (excelParams.get(i).getList().size() == 0) {
+ excelParams.remove(i);
+ } else {
+ excelParams.get(i).setOrderNum(i);
+ }
+ } else {
+ if (titlemap.containsKey(excelParams.get(i).getName())) {
+ excelParams.get(i).setOrderNum(i);
+ } else {
+ excelParams.remove(i);
+ }
+ }
+ }
+ sortAllParams(excelParams);
+ }
+
+ //-----------------update-begin-author:liusq---date:20220527--for: 以下方法是模板导出列循环功能新增的方法 ---
+ /**
+ * 先进行列的循环,因为涉及很多数据
+ *
+ * @param sheet
+ * @param map
+ */
+ private void colForeach(Sheet sheet, Map map) throws Exception {
+ Row row = null;
+ Cell cell = null;
+ int index = 0;
+ while (index <= sheet.getLastRowNum()) {
+ row = sheet.getRow(index++);
+ if (row == null) {
+ continue;
+ }
+ for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
+ cell = row.getCell(i);
+ if (row.getCell(i) != null && (cell.getCellType() == CellType.STRING
+ || cell.getCellType() == CellType.NUMERIC)) {
+ String text = PoiCellUtil.getCellValue(cell);
+ if (text.contains(FOREACH_COL) || text.contains(FOREACH_COL_VALUE)) {
+ foreachCol(cell, map, text);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 循环列表
+ *
+ * @param cell
+ * @param map
+ * @param name
+ * @throws Exception
+ */
+ private void foreachCol(Cell cell, Map map, String name) throws Exception {
+ boolean isCreate = name.contains(FOREACH_COL_VALUE);
+ name = name.replace(FOREACH_COL_VALUE, EMPTY).replace(FOREACH_COL, EMPTY).replace(START_STR,
+ EMPTY);
+ String[] keys = name.replaceAll("\\s{1,}", " ").trim().split(" ");
+ Collection> datas = (Collection>) PoiPublicUtil.getParamsValue(keys[0], map);
+ Object[] columnsInfo = getAllDataColumns(cell, name.replace(keys[0], EMPTY),
+ mergedRegionHelper);
+ if (datas == null) {
+ return;
+ }
+ Iterator> its = datas.iterator();
+ int rowspan = (Integer) columnsInfo[0], colspan = (Integer) columnsInfo[1];
+ @SuppressWarnings("unchecked")
+ List columns = (List) columnsInfo[2];
+ while (its.hasNext()) {
+ Object t = its.next();
+ setForeachRowCellValue(true, cell.getRow(), cell.getColumnIndex(), t, columns, map,
+ rowspan, colspan, mergedRegionHelper);
+ if (cell.getRow().getCell(cell.getColumnIndex() + colspan) == null) {
+ cell.getRow().createCell(cell.getColumnIndex() + colspan);
+ }
+ cell = cell.getRow().getCell(cell.getColumnIndex() + colspan);
+ }
+ if (isCreate) {
+ cell = cell.getRow().getCell(cell.getColumnIndex() - 1);
+ cell.setCellValue(cell.getStringCellValue() + END_STR);
+ }
+ }
+ /**
+ * 循环迭代创建,遍历row
+ *
+ * @param isCreate
+ * @param row
+ * @param columnIndex
+ * @param t
+ * @param columns
+ * @param map
+ * @param rowspan
+ * @param colspan
+ * @param mergedRegionHelper
+ * @return rowSize, cellSize
+ * @throws Exception
+ */
+ private int[] setForeachRowCellValue(boolean isCreate, Row row, int columnIndex, Object t,
+ List columns, Map map,
+ int rowspan, int colspan,
+ MergedRegionHelper mergedRegionHelper) throws Exception {
+ createRowCellSetStyle(row, columnIndex, columns, rowspan, colspan);
+ //填写数据
+ ExcelForEachParams params;
+ int loopSize = 1;
+ int loopCi = 1;
+ row = row.getSheet().getRow(row.getRowNum() - rowspan + 1);
+ for (int k = 0; k < rowspan; k++) {
+ int ci = columnIndex;
+ row.setHeight(getMaxHeight(k, colspan, columns));
+ for (int i = 0; i < colspan && i < columns.size(); i++) {
+ boolean isNumber = false;
+ params = columns.get(colspan * k + i);
+ tempCreateCellSet.add(row.getRowNum() + "_" + (ci));
+ if (params == null) {
+ continue;
+ }
+ if (StringUtils.isEmpty(params.getName())
+ && StringUtils.isEmpty(params.getConstValue())) {
+ row.getCell(ci).setCellStyle(params.getCellStyle());
+ ci = ci + params.getColspan();
+ continue;
+ }
+ String val;
+ Object obj = null;
+ //是不是常量
+ String tempStr = params.getName();
+ if (StringUtils.isEmpty(params.getName())) {
+ val = params.getConstValue();
+ } else {
+ if (isHasSymbol(tempStr, NUMBER_SYMBOL)) {
+ isNumber = true;
+ tempStr = tempStr.replaceFirst(NUMBER_SYMBOL, "");
+ }
+ map.put(teplateParams.getTempParams(), t);
+ boolean isDict = false;
+ String dict = null;
+ if (isHasSymbol(tempStr, DICT_HANDLER)) {
+ isDict = true;
+ dict = tempStr.substring(tempStr.indexOf(DICT_HANDLER) + 5).split(";")[0];
+ tempStr = tempStr.replaceFirst(DICT_HANDLER, "");
+ tempStr = tempStr.replaceFirst(dict + ";", "");
+ }
+ obj = eval(tempStr, map);
+ if (isDict && !(obj instanceof Collection)) {
+ obj = dictHandler.toName(dict, t, tempStr, obj);
+ }
+ val = obj.toString();
+ }
+ if (obj != null && obj instanceof Collection) {
+ // 需要找到哪一级别是集合 ,方便后面的replace
+ String collectName = evalFindName(tempStr, map);
+ int[] loop = setForEachLoopRowCellValue(row, ci, (Collection) obj, columns,
+ params, map, rowspan, colspan, mergedRegionHelper, collectName);
+ loopSize = Math.max(loopSize, loop[0]);
+ i += loop[1] - 1;
+ ci = loop[2] - params.getColspan();
+ } else if (obj != null && obj instanceof ImageEntity) {
+ ImageEntity img = (ImageEntity) obj;
+ row.getCell(ci).setCellValue("");
+ if (img.getRowspan() > 1 || img.getColspan() > 1) {
+ img.setHeight(0);
+ row.getCell(ci).getSheet().addMergedRegion(new CellRangeAddress(row.getCell(ci).getRowIndex(),
+ row.getCell(ci).getRowIndex() + img.getRowspan() - 1, row.getCell(ci).getColumnIndex(), row.getCell(ci).getColumnIndex() + img.getColspan() - 1));
+ }
+ createImageCell(row.getCell(ci), img.getHeight(), img.getRowspan(), img.getColspan(), img.getUrl(), img.getData());
+ } else if (isNumber && StringUtils.isNotEmpty(val)) {
+ row.getCell(ci).setCellValue(Double.parseDouble(val));
+ } else {
+ try {
+ row.getCell(ci).setCellValue(val);
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ }
+ }
+ if (params.getCellStyle() != null) {
+ row.getCell(ci).setCellStyle(params.getCellStyle());
+ }
+ //如果合并单元格,就把这个单元格的样式和之前的保持一致
+ setMergedRegionStyle(row, ci, params);
+ //合并对应单元格
+ //update-begin-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格,第二个表格读取错误 ---
+ boolean isNeedMerge = (params.getRowspan() != 1 || params.getColspan() != 1)
+ && !mergedRegionHelper.isMergedRegion(row.getRowNum() + 1, ci);
+ //update-end-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格,第二个表格读取错误 ---
+ if (isNeedMerge) {
+ PoiMergeCellUtil.addMergedRegion(row.getSheet(), row.getRowNum(),
+ row.getRowNum() + params.getRowspan() - 1, ci,
+ ci + params.getColspan() - 1);
+ }
+ ci = ci + params.getColspan();
+ }
+ loopCi = Math.max(loopCi, ci);
+ // 需要把需要合并的单元格合并了 --- 不是集合的栏位合并了
+ if (loopSize > 1) {
+ handlerLoopMergedRegion(row, columnIndex, columns, loopSize);
+ }
+ row = row.getSheet().getRow(row.getRowNum() + 1);
+ }
+ return new int[]{loopSize, loopCi};
+ }
+ /**
+ * 图片类型的Cell
+ */
+ public void createImageCell(Cell cell, double height, int rowspan, int colspan,
+ String imagePath, byte[] data) throws Exception {
+ if (height > cell.getRow().getHeight()) {
+ cell.getRow().setHeight((short) height);
+ }
+ ClientAnchor anchor;
+ if (type.equals(ExcelType.HSSF)) {
+ anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + colspan),
+ cell.getRow().getRowNum() + rowspan);
+ } else {
+ anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + colspan),
+ cell.getRow().getRowNum() + rowspan);
+ }
+ if (StringUtils.isNotEmpty(imagePath)) {
+ data = ImageCache.getImage(imagePath);
+ }
+ if (data != null) {
+ PoiExcelGraphDataUtil.getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
+ cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
+ }
+ }
+ /**
+ * 处理内循环
+ *
+ * @param row
+ * @param columnIndex
+ * @param obj
+ * @param columns
+ * @param params
+ * @param map
+ * @param rowspan
+ * @param colspan
+ * @param mergedRegionHelper
+ * @param collectName
+ * @return [rowNums, columnsNums, ciIndex]
+ * @throws Exception
+ */
+ private int[] setForEachLoopRowCellValue(Row row, int columnIndex, Collection obj, List columns,
+ ExcelForEachParams params, Map map,
+ int rowspan, int colspan,
+ MergedRegionHelper mergedRegionHelper, String collectName) throws Exception {
+
+ //多个一起遍历 -去掉第一层 把所有的数据遍历一遍
+ //STEP 1拿到所有的和当前一样项目的字段
+ List temp = getLoopEachParams(columns, columnIndex, collectName);
+ Iterator> its = obj.iterator();
+ Row tempRow = row;
+ int nums = 0;
+ int ci = columnIndex;
+ while (its.hasNext()) {
+ Object data = its.next();
+ map.put("loop_" + columnIndex, data);
+ int[] loopArr = setForeachRowCellValue(false, tempRow, columnIndex, data, temp, map, rowspan,
+ colspan, mergedRegionHelper);
+ nums += loopArr[0];
+ ci = Math.max(ci, loopArr[1]);
+ map.remove("loop_" + columnIndex);
+ tempRow = createRow(tempRow.getRowNum() + loopArr[0], row.getSheet(), false, rowspan);
+ }
+ for (int i = 0; i < temp.size(); i++) {
+ temp.get(i).setName(temp.get(i).getTempName().pop());
+ //都是集合
+ temp.get(i).setCollectCell(true);
+
+ }
+ return new int[]{nums, temp.size(), ci};
+ }
+ /**
+ * 创建并返回第一个Row
+ *
+ * @param sheet
+ * @param rowIndex
+ * @param isCreate
+ * @param rows
+ * @return
+ */
+ private Row createRow(int rowIndex, Sheet sheet, boolean isCreate, int rows) {
+ for (int i = 0; i < rows; i++) {
+ if (isCreate) {
+ sheet.createRow(rowIndex++);
+ } else if (sheet.getRow(rowIndex++) == null) {
+ sheet.createRow(rowIndex - 1);
+ }
+ }
+ return sheet.getRow(rowIndex - rows);
+ }
+ /**
+ * 根据 当前是集合的信息,把后面整个集合的迭代获取出来,并替换掉集合的前缀方便后面取数
+ *
+ * @param columns
+ * @param columnIndex
+ * @param collectName
+ * @return
+ */
+ private List getLoopEachParams(List columns, int columnIndex, String collectName) {
+ List temp = new ArrayList<>();
+ for (int i = 0; i < columns.size(); i++) {
+ //先置为不是集合
+ columns.get(i).setCollectCell(false);
+ if (columns.get(i) == null || columns.get(i).getName().contains(collectName)) {
+ temp.add(columns.get(i));
+ if (columns.get(i).getTempName() == null) {
+ columns.get(i).setTempName(new Stack<>());
+ }
+ columns.get(i).setCollectCell(true);
+ columns.get(i).getTempName().push(columns.get(i).getName());
+ columns.get(i).setName(columns.get(i).getName().replace(collectName, "loop_" + columnIndex));
+ }
+ }
+ return temp;
+ }
+
+ /**
+ * 设置行样式
+ * @param row
+ * @param columnIndex
+ * @param columns
+ * @param rowspan
+ * @param colspan
+ */
+ private void createRowCellSetStyle(Row row, int columnIndex, List columns,
+ int rowspan, int colspan) {
+ //所有的cell创建一遍
+ for (int i = 0; i < rowspan; i++) {
+ int size = columns.size();
+ for (int j = columnIndex, max = columnIndex + colspan; j < max; j++) {
+ if (row.getCell(j) == null) {
+ row.createCell(j);
+ CellStyle style = row.getRowNum() % 2 == 0
+ ? getStyles(false,
+ size <= j - columnIndex ? null : columns.get(j - columnIndex))
+ : getStyles(true,
+ size <= j - columnIndex ? null : columns.get(j - columnIndex));
+ //返回的styler不为空时才使用,否则使用Excel设置的,更加推荐Excel设置的样式
+ if (style != null) {
+ row.getCell(j).setCellStyle(style);
+ }
+ }
+
+ }
+ if (i < rowspan - 1) {
+ row = row.getSheet().getRow(row.getRowNum() + 1);
+ }
+ }
+ }
+
+ /**
+ * 获取CellStyle
+ * @param isSingle
+ * @param excelForEachParams
+ * @return
+ */
+ private CellStyle getStyles(boolean isSingle, ExcelForEachParams excelForEachParams) {
+ return excelExportStyler.getTemplateStyles(isSingle, excelForEachParams);
+ }
+
+ /**
+ * 获取最大高度
+ * @param k
+ * @param colspan
+ * @param columns
+ * @return
+ */
+ private short getMaxHeight(int k, int colspan, List columns) {
+ short high = columns.get(0).getHeight();
+ int n = k;
+ while (n > 0) {
+ if (columns.get(n * colspan).getHeight() == 0) {
+ n--;
+ } else {
+ high = columns.get(n * colspan).getHeight();
+ break;
+ }
+ }
+ return high;
+ }
+
+ private boolean isHasSymbol(String text, String symbol) {
+ return text.startsWith(symbol) || text.contains("{" + symbol)
+ || text.contains(" " + symbol);
+ }
+ /**
+ * 迭代把不是集合的数据都合并了
+ *
+ * @param row
+ * @param columnIndex
+ * @param columns
+ * @param loopSize
+ */
+ private void handlerLoopMergedRegion(Row row, int columnIndex, List columns, int loopSize) {
+ for (int i = 0; i < columns.size(); i++) {
+ if (!columns.get(i).isCollectCell()) {
+ PoiMergeCellUtil.addMergedRegion(row.getSheet(), row.getRowNum(),
+ row.getRowNum() + loopSize - 1, columnIndex,
+ columnIndex + columns.get(i).getColspan() - 1);
+ }
+ columnIndex = columnIndex + columns.get(i).getColspan();
+ }
+ }
+ /**
+ * 设置合并单元格的样式
+ *
+ * @param row
+ * @param ci
+ * @param params
+ */
+ private void setMergedRegionStyle(Row row, int ci, ExcelForEachParams params) {
+ //第一行数据
+ for (int i = 1; i < params.getColspan(); i++) {
+ if (params.getCellStyle() != null) {
+ row.getCell(ci + i).setCellStyle(params.getCellStyle());
+ }
+ }
+ for (int i = 1; i < params.getRowspan(); i++) {
+ for (int j = 0; j < params.getColspan(); j++) {
+ if (params.getCellStyle() != null) {
+ row.getCell(ci + j).setCellStyle(params.getCellStyle());
+ }
+ }
+ }
+ }
+ /**
+ * 获取迭代的数据的值
+ *
+ * @param cell
+ * @param name
+ * @param mergedRegionHelper
+ * @return
+ */
+ private Object[] getAllDataColumns(Cell cell, String name,
+ MergedRegionHelper mergedRegionHelper) {
+ List columns = new ArrayList();
+ cell.setCellValue("");
+ columns.add(getExcelTemplateParams(name.replace(END_STR, EMPTY), cell, mergedRegionHelper));
+ int rowspan = 1, colspan = 1;
+ if (!name.contains(END_STR)) {
+ int index = cell.getColumnIndex();
+ //保存col 的开始列
+ int startIndex = cell.getColumnIndex();
+ Row row = cell.getRow();
+ while (index < row.getLastCellNum()) {
+ int colSpan = columns.get(columns.size() - 1) != null
+ ? columns.get(columns.size() - 1).getColspan() : 1;
+ index += colSpan;
+
+
+ for (int i = 1; i < colSpan; i++) {
+ //添加合并的单元格,这些单元可能不是空,但是没有值,所以也需要跳过
+ columns.add(null);
+ continue;
+ }
+ cell = row.getCell(index);
+ //可能是合并的单元格
+ if (cell == null) {
+ //读取是判断,跳过
+ columns.add(null);
+ continue;
+ }
+ String cellStringString;
+ try {//不允许为空 便利单元格必须有结尾和值
+ cellStringString = cell.getStringCellValue();
+ if (StringUtils.isBlank(cellStringString) && colspan + startIndex <= index) {
+ throw new ExcelExportException("for each 当中存在空字符串,请检查模板");
+ } else if (StringUtils.isBlank(cellStringString)
+ && colspan + startIndex > index) {
+ //读取是判断,跳过,数据为空,但是不是第一次读这一列,所以可以跳过
+ columns.add(new ExcelForEachParams(null, cell.getCellStyle(), (short) 0));
+ continue;
+ }
+ } catch (Exception e) {
+ throw new ExcelExportException(ExcelExportEnum.TEMPLATE_ERROR, e);
+ }
+ //把读取过的cell 置为空
+ cell.setCellValue("");
+ if (cellStringString.contains(END_STR)) {
+ columns.add(getExcelTemplateParams(cellStringString.replace(END_STR, EMPTY),
+ cell, mergedRegionHelper));
+ //补全缺失的cell(合并单元格后面的)
+ int lastCellColspan = columns.get(columns.size() - 1).getColspan();
+ for (int i = 1; i < lastCellColspan; i++) {
+ //添加合并的单元格,这些单元可能不是空,但是没有值,所以也需要跳过
+ columns.add(null);
+ }
+ break;
+ } else if (cellStringString.contains(WRAP)) {
+ columns.add(getExcelTemplateParams(cellStringString.replace(WRAP, EMPTY), cell,
+ mergedRegionHelper));
+ //发现换行符,执行换行操作
+ colspan = index - startIndex + 1;
+ index = startIndex - columns.get(columns.size() - 1).getColspan();
+ row = row.getSheet().getRow(row.getRowNum() + 1);
+ rowspan++;
+ } else {
+ columns.add(getExcelTemplateParams(cellStringString.replace(WRAP, EMPTY), cell,
+ mergedRegionHelper));
+ }
+ }
+ }
+ colspan = 0;
+ for (int i = 0; i < columns.size(); i++) {
+ colspan += columns.get(i) != null ? columns.get(i).getColspan() : 0;
+ }
+ colspan = colspan / rowspan;
+ return new Object[]{rowspan, colspan, columns};
+ }
+ /**
+ * 获取模板参数
+ *
+ * @param name
+ * @param cell
+ * @param mergedRegionHelper
+ * @return
+ */
+ private ExcelForEachParams getExcelTemplateParams(String name, Cell cell,
+ MergedRegionHelper mergedRegionHelper) {
+ name = name.trim();
+ ExcelForEachParams params = new ExcelForEachParams(name, cell.getCellStyle(),
+ cell.getRow().getHeight());
+ //判断是不是常量
+ if (name.startsWith(CONST) && name.endsWith(CONST)) {
+ params.setName(null);
+ params.setConstValue(name.substring(1, name.length() - 1));
+ }
+ //判断是不是空
+ if (NULL.equals(name)) {
+ params.setName(null);
+ params.setConstValue(EMPTY);
+ }
+ //获取合并单元格的数据
+ if (mergedRegionHelper.isMergedRegion(cell.getRowIndex() + 1, cell.getColumnIndex())) {
+ Integer[] colAndrow = mergedRegionHelper.getRowAndColSpan(cell.getRowIndex() + 1,
+ cell.getColumnIndex());
+ params.setRowspan(colAndrow[0]);
+ params.setColspan(colAndrow[1]);
+ }
+ return params;
+ }
+ //-----------------update-end-author:liusq---date:20220527--for: 以上方法是模板导出列循环功能新增的方法 ---
+}
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/graph/builder/ExcelChartBuildService.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/graph/builder/ExcelChartBuildService.java
new file mode 100644
index 0000000..0346c55
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/graph/builder/ExcelChartBuildService.java
@@ -0,0 +1,263 @@
+///**
+// *
+// */
+//package org.jeecgframework.poi.excel.graph.builder;
+//
+//import java.util.ArrayList;
+//import java.util.List;
+//
+//import org.apache.commons.lang3.StringUtils;
+//import org.apache.poi.ss.usermodel.Chart;
+//import org.apache.poi.ss.usermodel.ClientAnchor;
+//import org.apache.poi.ss.usermodel.Drawing;
+//import org.apache.poi.ss.usermodel.Sheet;
+//import org.apache.poi.ss.usermodel.Workbook;
+//import org.apache.poi.ss.usermodel.charts.AxisCrosses;
+//import org.apache.poi.ss.usermodel.charts.AxisPosition;
+//import org.apache.poi.ss.usermodel.charts.ChartAxis;
+//import org.apache.poi.ss.usermodel.charts.ChartDataSource;
+//import org.apache.poi.ss.usermodel.charts.ChartLegend;
+//import org.apache.poi.ss.usermodel.charts.DataSources;
+//import org.apache.poi.ss.usermodel.charts.LegendPosition;
+//import org.apache.poi.ss.usermodel.charts.LineChartData;
+//import org.apache.poi.ss.usermodel.charts.ScatterChartData;
+//import org.apache.poi.ss.usermodel.charts.ValueAxis;
+//import org.apache.poi.ss.util.CellRangeAddress;
+//
+//import org.jeecgframework.poi.excel.graph.constant.ExcelGraphElementType;
+//import org.jeecgframework.poi.excel.graph.constant.ExcelGraphType;
+//import org.jeecgframework.poi.excel.graph.entity.ExcelGraph;
+//import org.jeecgframework.poi.excel.graph.entity.ExcelGraphElement;
+//import org.jeecgframework.poi.excel.graph.entity.ExcelTitleCell;
+//import org.jeecgframework.poi.util.PoiCellUtil;
+//import org.jeecgframework.poi.util.PoiExcelGraphDataUtil;
+//
+///**
+// * @Description
+// * @author liusq
+// * @data 2022年1月4号
+// */
+//public class ExcelChartBuildService
+//{
+// /**
+// *
+// * @param workbook
+// * @param graphList
+// * @param build 通过实时数据行来重新计算图形定义
+// * @param append
+// */
+// public static void createExcelChart(Workbook workbook, List graphList, Boolean build, Boolean append)
+// {
+// if(workbook!=null&&graphList!=null){
+// //设定默认第一个sheet为数据项
+// Sheet dataSouce=workbook.getSheetAt(0);
+// if(dataSouce!=null){
+// buildTitle(dataSouce,graphList);
+//
+// if(build){
+// PoiExcelGraphDataUtil.buildGraphData(dataSouce, graphList);
+// }
+// if(append){
+// buildExcelChart(dataSouce, dataSouce, graphList);
+// }else{
+// Sheet sheet=workbook.createSheet("图形界面");
+// buildExcelChart(dataSouce, sheet, graphList);
+// }
+// }
+//
+// }
+// }
+//
+// /**
+// * 构建基础图形
+// * @param drawing
+// * @param anchor
+// * @param dataSourceSheet
+// * @param graph
+// */
+// private static void buildExcelChart(Drawing drawing,ClientAnchor anchor,Sheet dataSourceSheet,ExcelGraph graph){
+// Chart chart = null;
+// // TODO 图表没有成功
+// //drawing.createChart(anchor);
+// ChartLegend legend = chart.getOrCreateLegend();
+// legend.setPosition(LegendPosition.TOP_RIGHT);
+//
+// ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM);
+// ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
+// leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
+// ExcelGraphElement categoryElement=graph.getCategory();
+//
+// ChartDataSource categoryChart;
+// if(categoryElement!=null&& categoryElement.getElementType().equals(ExcelGraphElementType.STRING_TYPE)){
+// categoryChart=DataSources.fromStringCellRange(dataSourceSheet, new CellRangeAddress(categoryElement.getStartRowNum(),categoryElement.getEndRowNum(),categoryElement.getStartColNum(),categoryElement.getEndColNum()));
+// }else{
+// categoryChart=DataSources.fromNumericCellRange(dataSourceSheet, new CellRangeAddress(categoryElement.getStartRowNum(),categoryElement.getEndRowNum(),categoryElement.getStartColNum(),categoryElement.getEndColNum()));
+// }
+//
+// List valueList=graph.getValueList();
+// List> chartValueList= new ArrayList<>();
+// if(valueList!=null&&valueList.size()>0){
+// for(ExcelGraphElement ele:valueList){
+// ChartDataSource source=DataSources.fromNumericCellRange(dataSourceSheet, new CellRangeAddress(ele.getStartRowNum(),ele.getEndRowNum(),ele.getStartColNum(),ele.getEndColNum()));
+// chartValueList.add(source);
+// }
+// }
+//
+// if(graph.getGraphType().equals(ExcelGraphType.LINE_CHART)){
+// LineChartData data = chart.getChartDataFactory().createLineChartData();
+// buildLineChartData(data, categoryChart, chartValueList, graph.getTitle());
+// chart.plot(data, bottomAxis, leftAxis);
+// }
+// else
+// {
+// ScatterChartData data=chart.getChartDataFactory().createScatterChartData();
+// buildScatterChartData(data, categoryChart, chartValueList,graph.getTitle());
+// chart.plot(data, bottomAxis, leftAxis);
+// }
+// }
+//
+//
+//
+//
+// /**
+// * 构建多个图形对象
+// * @param dataSourceSheet
+// * @param tragetSheet
+// * @param graphList
+// */
+// private static void buildExcelChart(Sheet dataSourceSheet,Sheet tragetSheet,List graphList){
+// int len=graphList.size();
+// if(len==1)
+// {
+// buildExcelChart(dataSourceSheet, tragetSheet, graphList.get(0));
+// }
+// else
+// {
+// int drawStart=0;
+// int drawEnd=20;
+// Drawing drawing = PoiExcelGraphDataUtil.getDrawingPatriarch(tragetSheet);
+// for(int i=0;i0){
+//
+// }else{
+// for(int i=0;i graphList){
+// if(graphList!=null&&graphList.size()>0){
+// for(ExcelGraph graph:graphList){
+// if(graph!=null)
+// {
+// buildTitle(sheet, graph);
+// }
+// }
+// }
+// }
+//
+// /**
+// *
+// * @param data
+// * @param categoryChart
+// * @param chartValueList
+// * @param title
+// */
+// private static void buildLineChartData(LineChartData data,ChartDataSource categoryChart,List> chartValueList,List title){
+// if(chartValueList.size()==title.size())
+// {
+// int len=title.size();
+// for(int i=0;i source:chartValueList){
+// String temp_title=title.get(i);
+// if(StringUtils.isNotBlank(temp_title)){
+// //data.addSerie(categoryChart, source).setTitle(_title);
+// }else{
+// //data.addSerie(categoryChart, source);
+// }
+// }
+// }
+// }
+//
+// /**
+// *
+// * @param data
+// * @param categoryChart
+// * @param chartValueList
+// * @param title
+// */
+// private static void buildScatterChartData(ScatterChartData data,ChartDataSource categoryChart,List> chartValueList,List title){
+// if(chartValueList.size()==title.size())
+// {
+// int len=title.size();
+// for(int i=0;i source:chartValueList){
+// String temp_title=title.get(i);
+// if(StringUtils.isNotBlank(temp_title)){
+// data.addSerie(categoryChart, source).setTitle(temp_title);
+// }else{
+// data.addSerie(categoryChart, source);
+// }
+// }
+// }
+// }
+//
+//
+//}
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/CellValueHelper.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/CellValueHelper.java
new file mode 100644
index 0000000..51d4204
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/CellValueHelper.java
@@ -0,0 +1,135 @@
+package org.jeecgframework.poi.excel.html.helper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFFont;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import com.google.common.xml.XmlEscapers;
+
+/**
+ * Cell值帮助类
+ *
+ * @author JEECG
+ * @date 2015年5月9日 下午10:31:32
+ */
+public class CellValueHelper {
+ /**
+ * Excel 格式
+ */
+ private boolean is07;
+
+ private int cssRandom;
+
+ private Map fontCache = new HashMap();
+
+ public CellValueHelper(Workbook wb, int cssRandom) {
+ this.cssRandom = cssRandom;
+ if (wb instanceof HSSFWorkbook)
+ is07 = false;
+ else if (wb instanceof XSSFWorkbook) {
+ is07 = true;
+ cacheFontInfo(wb);
+ } else
+ throw new IllegalArgumentException("unknown workbook type: " + wb.getClass().getSimpleName());
+ }
+
+ /**
+ * O7 版本坑爹bug
+ *
+ * @param wb
+ */
+ private void cacheFontInfo(Workbook wb) {
+ for (int i = 0, le = wb.getNumberOfFonts(); i < le; i++) {
+ Font font = wb.getFontAt(i);
+ fontCache.put(font.getBold() + "_" + font.getItalic() + "_" + font.getFontName() + "_" + font.getFontHeightInPoints() + "_" + font.getColor(), font.getIndex() + "");
+ }
+
+ }
+
+ public String getHtmlValue(Cell cell) {
+ if (CellType.BOOLEAN == cell.getCellType() || CellType.NUMERIC == cell.getCellType()) {
+ cell.setCellType( CellType.STRING);
+ return cell.getStringCellValue();
+ } else if ( CellType.STRING == cell.getCellType()) {
+ if (cell.getRichStringCellValue().numFormattingRuns() == 0) {
+ return XmlEscapers.xmlContentEscaper().escape(cell.getStringCellValue());
+ } else if (is07) {
+ return getXSSFRichString((XSSFRichTextString) cell.getRichStringCellValue());
+ } else {
+ return getHSSFRichString((HSSFRichTextString) cell.getRichStringCellValue());
+ }
+ }
+ return "";
+ }
+
+ /**
+ * 03版本复杂数据
+ *
+ * @param rich
+ * @return
+ */
+ private String getHSSFRichString(HSSFRichTextString rich) {
+ int nums = rich.numFormattingRuns();
+ StringBuilder sb = new StringBuilder();
+ String text = rich.toString();
+ int currentIndex = 0;
+ sb.append(text.substring(0, rich.getIndexOfFormattingRun(0)));
+ for (int i = 0; i < nums; i++) {
+ sb.append("");
+ currentIndex = rich.getIndexOfFormattingRun(i);
+ if (i < nums - 1) {
+ sb.append(XmlEscapers.xmlContentEscaper().escape(text.substring(currentIndex, rich.getIndexOfFormattingRun(i + 1))));
+ } else {
+ sb.append(XmlEscapers.xmlContentEscaper().escape(text.substring(currentIndex, text.length())));
+ }
+ sb.append("");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 07版本复杂数据
+ *
+ * @param rich
+ * @return
+ */
+ private String getXSSFRichString(XSSFRichTextString rich) {
+ int nums = rich.numFormattingRuns();
+ StringBuilder sb = new StringBuilder();
+ String text = rich.toString();
+ int currentIndex = 0, lastIndex = 0;
+ for (int i = 1; i <= nums; i++) {
+ sb.append("");
+ currentIndex = rich.getIndexOfFormattingRun(i) == -1 ? text.length() : rich.getIndexOfFormattingRun(i);
+ sb.append(XmlEscapers.xmlContentEscaper().escape(text.substring(lastIndex, currentIndex)));
+ sb.append("");
+ lastIndex = currentIndex;
+ }
+ return sb.toString();
+ }
+
+ private String getFontIndex(XSSFFont font) {
+ return fontCache.get(font.getBold() + "_" + font.getItalic() + "_" + font.getFontName() + "_" + font.getFontHeightInPoints() + "_" + font.getColor());
+ }
+}
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/StylerHelper.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/StylerHelper.java
new file mode 100644
index 0000000..734126c
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/StylerHelper.java
@@ -0,0 +1,259 @@
+package org.jeecgframework.poi.excel.html.helper;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Formatter;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFPalette;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFColor;
+import org.apache.poi.xssf.usermodel.XSSFFont;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.jeecgframework.poi.util.PoiPublicUtil;
+
+/**
+ * 样式帮助类
+ *
+ * @author JEECG
+ * @date 2015年5月9日 下午4:04:24
+ */
+public class StylerHelper {
+
+ private static String DEFAULTS_CLASS_CSS = ".excelDefaults {background-color: white;color: black;text-decoration: none;direction: ltr;text-transform: none;text-indent: 0;letter-spacing: 0;word-spacing: 0;white-space: normal;unicode-bidi: normal;vertical-align: 0;text-shadow: none;padding: 0;margin: 0;border-collapse: collapse;white-space: pre-wrap;word-wrap: break-word;word-break: break-all;}.excelDefaults td {padding: 1px 5px;border: 1px solid silver;border-color: #000000;text-align: center;vertical-align: middle;font-size: 12pt;}.excelDefaults .colHeader {background-color: silver;font-weight: bold;border: 1px solid black;text-align: center;padding: 1px 5px;}.excelDefaults .rowHeader {background-color: silver;font-weight: bold;border: 1px solid black;text-align: right;padding: 1px 5px;}";
+
+ private static final String DEFAULTS_CLASS = "excelDefaults";
+
+ private static final Map ALIGN = PoiPublicUtil.mapFor(HorizontalAlignment.LEFT.getCode(), "left",HorizontalAlignment.CENTER.getCode(), "center",HorizontalAlignment.RIGHT.getCode(), "right", HorizontalAlignment.FILL.getCode(), "left",HorizontalAlignment.JUSTIFY.getCode(), "left",HorizontalAlignment.CENTER_SELECTION.getCode(), "center");
+
+ private static final Map VERTICAL_ALIGN = PoiPublicUtil.mapFor(VerticalAlignment.BOTTOM.getCode(), "bottom", VerticalAlignment.CENTER.getCode(), "middle",VerticalAlignment.TOP.getCode(), "top");
+
+ private Formatter out;
+
+ private Sheet sheet;
+
+ private HtmlHelper helper;
+
+ private int sheetNum;
+
+ private int cssRandom;
+
+ public StylerHelper(Workbook wb, Formatter out, int sheetNum, int cssRandom) {
+ this.out = out;
+ this.sheetNum = sheetNum;
+ this.cssRandom = cssRandom;
+ if (wb instanceof HSSFWorkbook)
+ helper = new HSSFHtmlHelper((HSSFWorkbook) wb);
+ else if (wb instanceof XSSFWorkbook)
+ helper = new XSSFHtmlHelper((XSSFWorkbook) wb);
+ else
+ throw new IllegalArgumentException("unknown workbook type: " + wb.getClass().getSimpleName());
+ printInlineStyle(wb);
+ }
+
+ private void printInlineStyle(Workbook wb) {
+ out.format("%n");
+ }
+
+ private void prontFonts(Workbook wb) {
+ //update-begin---author:liusq Date:20220228 for:[I4I3ZY]issue AutoPOi Workbook对象转HTML字符串 数组下标越界异常----
+ for (int i = 0, le = wb.getNumberOfFonts(); i < le; i++) {
+ Font font = wb.getFontAt(i);
+ out.format(".%s .%s {%n", DEFAULTS_CLASS, "font_" + i + "_" + cssRandom);
+ fontStyle(font);
+ out.format("}%n");
+ }
+ //update-end---author:liusq Date:20220228 for:[I4I3ZY]issue AutoPOi Workbook对象转HTML字符串 数组下标越界异常整----
+ }
+
+ public void printStyles(Workbook wb) {
+ if (DEFAULTS_CLASS_CSS == null) {
+ DEFAULTS_CLASS_CSS = getDefaultsClassCss();
+ }
+ out.format(DEFAULTS_CLASS_CSS);
+ Set seen = new HashSet();
+ sheet = wb.getSheetAt(sheetNum);
+ Iterator rows = sheet.rowIterator();
+ while (rows.hasNext()) {
+ Row row = rows.next();
+ for (Cell cell : row) {
+ CellStyle style = cell.getCellStyle();
+ if (!seen.contains(style)) {
+ printStyle(style);
+ seen.add(style);
+ }
+ }
+ }
+ }
+
+ private String getDefaultsClassCss() {
+ BufferedReader in = null;
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb);
+ try {
+ in = new BufferedReader(new InputStreamReader(StylerHelper.class.getResourceAsStream("excelStyle.css")));
+ String line;
+ while ((line = in.readLine()) != null) {
+ formatter.format("%s%n", line);
+ }
+ return formatter.toString();
+ } catch (IOException e) {
+ throw new IllegalStateException("Reading standard css", e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ throw new IllegalStateException("Reading standard css", e);
+ }
+ }
+ formatter.close();
+ }
+ }
+
+ private void printStyle(CellStyle style) {
+ out.format(".%s .%s {%n", DEFAULTS_CLASS, styleName(style));
+ styleContents(style);
+ out.format("}%n");
+ }
+
+ private void styleContents(CellStyle style) {
+ if (style.getAlignment().getCode() != 2) {
+ styleOut("text-align", style.getAlignment().getCode(), ALIGN);
+ styleOut("vertical-align", style.getAlignment().getCode(), VERTICAL_ALIGN);
+ }
+ helper.colorStyles(style, out);
+ }
+
+ private void fontStyle(Font font) {
+ if (font.getBold())
+ out.format(" font-weight: bold;%n");
+ if (font.getItalic())
+ out.format(" font-style: italic;%n");
+ out.format(" font-family: %s;%n", font.getFontName());
+
+ int fontheight = font.getFontHeightInPoints();
+ if (fontheight == 9) {
+ fontheight = 10;
+ }
+ out.format(" font-size: %dpt;%n", fontheight);
+ helper.styleColor(out, "color", getColor(font));
+ }
+
+ private Color getColor(Font font) {
+ if (helper instanceof HSSFHtmlHelper) {
+ return ((HSSFWorkbook) sheet.getWorkbook()).getCustomPalette().getColor(font.getColor());
+ } else {
+ return ((XSSFFont) font).getXSSFColor();
+ }
+ }
+
+ private String styleName(CellStyle style) {
+ if (style == null)
+ return "";
+ return String.format("style_%02x_%s", style.getIndex(), cssRandom);
+ }
+
+ private void styleOut(String attr, K key, Map mapping) {
+ String value = mapping.get(key);
+ if (value != null) {
+ out.format(" %s: %s;%n", attr, value);
+ }
+ }
+
+ private interface HtmlHelper {
+ /**
+ * Outputs the appropriate CSS style for the given cell style.
+ *
+ * @param style
+ * The cell style.
+ * @param out
+ * The place to write the output.
+ */
+ void colorStyles(CellStyle style, Formatter out);
+
+ void styleColor(Formatter out, String attr, Color color);
+ }
+
+ private class HSSFHtmlHelper implements HtmlHelper {
+ private final HSSFWorkbook wb;
+ private final HSSFPalette colors;
+
+ //-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------
+ private HSSFColor HSSF_AUTO = new HSSFColor(0x40, -1, java.awt.Color.black);
+ //-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------
+
+ public HSSFHtmlHelper(HSSFWorkbook wb) {
+ this.wb = wb;
+ colors = wb.getCustomPalette();
+ }
+
+ public void colorStyles(CellStyle style, Formatter out) {
+ HSSFCellStyle cs = (HSSFCellStyle) style;
+ out.format(" /* fill pattern = %d */%n", cs.getFillPattern());
+ styleColor(out, "background-color", cs.getFillForegroundColor());
+ styleColor(out, "color", colors.getColor(cs.getFont(wb).getColor()));
+ }
+
+ private void styleColor(Formatter out, String attr, short index) {
+ HSSFColor color = colors.getColor(index);
+ if (index == HSSF_AUTO.getIndex() || color == null) {
+ out.format(" /* %s: index = %d */%n", attr, index);
+ } else {
+ short[] rgb = color.getTriplet();
+ out.format(" %s: #%02x%02x%02x; /* index = %d */%n", attr, rgb[0], rgb[1], rgb[2], index);
+ }
+ }
+
+ public void styleColor(Formatter out, String attr, Color color) {
+ if (color == null) {
+ return;
+ }
+ HSSFColor hSSFColor = (HSSFColor) color;
+ short[] rgb = hSSFColor.getTriplet();
+ out.format(" %s: #%02x%02x%02x; %n", attr, rgb[0], rgb[1], rgb[2]);
+ }
+ }
+
+ /**
+ * Implementation of {@link HtmlHelper} for XSSF files.
+ *
+ * @author Ken Arnold, Industrious Media LLC
+ */
+ private class XSSFHtmlHelper implements HtmlHelper {
+
+ public XSSFHtmlHelper(XSSFWorkbook wb) {
+ }
+
+ public void colorStyles(CellStyle style, Formatter out) {
+ XSSFCellStyle cs = (XSSFCellStyle) style;
+ styleColor(out, "background-color", cs.getFillForegroundXSSFColor());
+ styleColor(out, "color", cs.getFont().getXSSFColor());
+ }
+
+ public void styleColor(Formatter out, String attr, Color color) {
+ XSSFColor xSSFColor = (XSSFColor) color;
+ if (color == null || xSSFColor.isAuto())
+ return;
+
+ byte[] rgb = xSSFColor.getRGB();
+ if (rgb == null) {
+ return;
+ }
+ out.format(" %s: #%02x%02x%02x;%n", attr, rgb[0], rgb[1], rgb[2]);
+ }
+ }
+
+}
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/CellValueServer.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/CellValueServer.java
new file mode 100644
index 0000000..23e1910
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/CellValueServer.java
@@ -0,0 +1,387 @@
+/**
+ * Copyright 2013-2015 JEECG (jeecgos@163.com)
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 org.jeecgframework.poi.excel.imports;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;
+import org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity;
+import org.jeecgframework.poi.exception.excel.ExcelImportException;
+import org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum;
+import org.jeecgframework.poi.handler.inter.IExcelDataHandler;
+import org.jeecgframework.poi.util.ExcelUtil;
+import org.jeecgframework.poi.util.PoiPublicUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.Time;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Cell 取值服务 判断类型处理数据 1.判断Excel中的类型 2.根据replace替换值 3.handler处理数据 4.判断返回类型转化数据返回
+ *
+ * @author JEECG
+ * @date 2014年6月26日 下午10:42:28
+ */
+public class CellValueServer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CellValueServer.class);
+
+ private List hanlderList = null;
+
+ /**
+ * 获取单元格内的值
+ *
+ * @param xclass
+ * @param cell
+ * @param entity
+ * @return
+ */
+ private Object getCellValue(String xclass, Cell cell, ExcelImportEntity entity) {
+ if (cell == null) {
+ return "";
+ }
+ Object result = null;
+ // 日期格式比较特殊,和cell格式不一致
+ if ("class java.util.Date".equals(xclass) || ("class java.sql.Time").equals(xclass)) {
+ if ( CellType.NUMERIC == cell.getCellType()) {
+ // 日期格式
+ result = cell.getDateCellValue();
+ } else {
+ cell.setCellType( CellType.STRING);
+ result = getDateData(entity, cell.getStringCellValue());
+ }
+ if (("class java.sql.Time").equals(xclass)) {
+ result = new Time(((Date) result).getTime());
+ }
+ } else if ( CellType.NUMERIC == cell.getCellType()) {
+ result = cell.getNumericCellValue();
+ } else if ( CellType.BOOLEAN == cell.getCellType()) {
+ result = cell.getBooleanCellValue();
+ } else if ( CellType.FORMULA == cell.getCellType() && PoiPublicUtil.isNumber(xclass)) {
+ //如果单元格是表达式 且 字段是数字类型
+ double cellValue = cell.getNumericCellValue();
+ //---author:liusq---date:20221102-----for: [issues/3369]Excel导入 带公式的时候精度丢失---
+ //setScale方法的第一个参数设置小数点保留位数,第二个参数设置进位方法、此处是四舍五入
+ BigDecimal bigDecimal= new BigDecimal(cellValue).setScale(4, RoundingMode.HALF_UP);
+ //stripTrailingZeros方法去除末尾的0,toPlainString避免输出科学计数法的字符串
+ result = bigDecimal.stripTrailingZeros().toPlainString();
+ //---author:liusq---date:20221102-----for:[issues/3369] Excel导入 带公式的时候精度丢失---
+ } else {
+ //设置单元格类型
+ cell.setCellType(CellType.STRING);
+ result = cell.getStringCellValue();
+ }
+ return result;
+ }
+
+ /**
+ * 获取日期类型数据
+ *
+ * @Author JEECG
+ * @date 2013年11月26日
+ * @param entity
+ * @param value
+ * @return
+ */
+ private Date getDateData(ExcelImportEntity entity, String value) {
+ if (StringUtils.isNotEmpty(entity.getFormat()) && StringUtils.isNotEmpty(value)) {
+ SimpleDateFormat format = new SimpleDateFormat(entity.getFormat());
+ try {
+ return format.parse(value);
+ } catch (ParseException e) {
+ LOGGER.error("时间格式化失败,格式化:{},值:{}", entity.getFormat(), value);
+ throw new ExcelImportException(ExcelImportEnum.GET_VALUE_ERROR);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 获取cell的值
+ *
+ * @param object
+ * @param excelParams
+ * @param cell
+ * @param titleString
+ */
+ public Object getValue(IExcelDataHandler dataHanlder, Object object, Cell cell, Map excelParams, String titleString) throws Exception {
+ ExcelImportEntity entity = excelParams.get(titleString);
+ String xclass = "class java.lang.Object";
+ if (!(object instanceof Map)) {
+ Method setMethod = entity.getMethods() != null && entity.getMethods().size() > 0 ? entity.getMethods().get(entity.getMethods().size() - 1) : entity.getMethod();
+ Type[] ts = setMethod.getGenericParameterTypes();
+ xclass = ts[0].toString();
+ }
+ Object result = getCellValue(xclass, cell, entity);
+ if (entity != null) {
+ result = hanlderSuffix(entity.getSuffix(), result);
+ //update-begin-author:taoYan date:20180807 for:多值替换
+ result = replaceValue(entity.getReplace(), result,entity.isMultiReplace());
+ //update-end-author:taoYan date:20180807 for:多值替换
+ }
+ result = hanlderValue(dataHanlder, object, result, titleString);
+ return getValueByType(xclass, result, entity);
+ }
+
+ /**
+ * 获取cell值
+ *
+ * @param dataHanlder
+ * @param object
+ * @param cellEntity
+ * @param excelParams
+ * @param titleString
+ * @return
+ */
+ public Object getValue(IExcelDataHandler dataHanlder, Object object, SaxReadCellEntity cellEntity, Map excelParams, String titleString) {
+ ExcelImportEntity entity = excelParams.get(titleString);
+ Method setMethod = entity.getMethods() != null && entity.getMethods().size() > 0 ? entity.getMethods().get(entity.getMethods().size() - 1) : entity.getMethod();
+ Type[] ts = setMethod.getGenericParameterTypes();
+ String xclass = ts[0].toString();
+ Object result = cellEntity.getValue();
+ result = hanlderSuffix(entity.getSuffix(), result);
+ //update-begin-auhtor:taoyan date:20180807 for:多值替换
+ result = replaceValue(entity.getReplace(), result,entity.isMultiReplace());
+ //update-end-auhtor:taoyan date:20180807 for:多值替换
+ result = hanlderValue(dataHanlder, object, result, titleString);
+ return getValueByType(xclass, result, entity);
+ }
+
+ /**
+ * 把后缀删除掉
+ *
+ * @param result
+ * @param suffix
+ * @return
+ */
+ private Object hanlderSuffix(String suffix, Object result) {
+ if (StringUtils.isNotEmpty(suffix) && result != null && result.toString().endsWith(suffix)) {
+ String temp = result.toString();
+ return temp.substring(0, temp.length() - suffix.length());
+ }
+ return result;
+ }
+
+ /**
+ * 根据返回类型获取返回值
+ *
+ * @param xclass
+ * @param result
+ * @param entity
+ * @return
+ */
+ private Object getValueByType(String xclass, Object result, ExcelImportEntity entity) {
+ try {
+ //update-begin-author:scott date:20180711 for:TASK #2950 【bug】excel 导入报错,空指针问题
+ if(result==null || "".equals(String.valueOf(result))){
+ return null;
+ }
+ //update-end-author:scott date:20180711 for:TASK #2950 【bug】excel 导入报错,空指针问题
+ if ("class java.util.Date".equals(xclass)) {
+ return result;
+ }
+ if ("class java.lang.Boolean".equals(xclass) || "boolean".equals(xclass)) {
+ //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ Boolean temp = Boolean.valueOf(String.valueOf(result));
+ //if(StringUtils.isNotEmpty(entity.getNumFormat())){
+ // return Boolean.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));
+ //}else{
+ return temp;
+ //}
+ //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ }
+ if ("class java.lang.Double".equals(xclass) || "double".equals(xclass)) {
+ //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ Double temp = Double.valueOf(String.valueOf(result));
+ //if(StringUtils.isNotEmpty(entity.getNumFormat())){
+ // return Double.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));
+ //}else{
+ return temp;
+ //}
+ //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ }
+ if ("class java.lang.Long".equals(xclass) || "long".equals(xclass)) {
+ //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ Long temp = Long.valueOf(ExcelUtil.remove0Suffix(String.valueOf(result)));
+ //if(StringUtils.isNotEmpty(entity.getNumFormat())){
+ // return Long.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));
+ //}else{
+ return temp;
+ //}
+ //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ }
+ if ("class java.lang.Float".equals(xclass) || "float".equals(xclass)) {
+ //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ Float temp = Float.valueOf(String.valueOf(result));
+ //if(StringUtils.isNotEmpty(entity.getNumFormat())){
+ // return Float.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));
+ //}else{
+ return temp;
+ //}
+ //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ }
+ if ("class java.lang.Integer".equals(xclass) || "int".equals(xclass)) {
+ //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ Integer temp = Integer.valueOf(ExcelUtil.remove0Suffix(String.valueOf(result)));
+ //if(StringUtils.isNotEmpty(entity.getNumFormat())){
+ // return Integer.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));
+ //}else{
+ return temp;
+ //}
+ //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ }
+ if ("class java.math.BigDecimal".equals(xclass)) {
+ //update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ BigDecimal temp = new BigDecimal(String.valueOf(result));
+ //if(StringUtils.isNotEmpty(entity.getNumFormat())){
+ // return new BigDecimal(new DecimalFormat(entity.getNumFormat()).format(temp));
+ //}else{
+ return temp;
+ //}
+ //update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970
+ }
+ if ("class java.lang.String".equals(xclass)) {
+ // 针对String 类型,但是Excel获取的数据却不是String,比如Double类型,防止科学计数法
+ if (result instanceof String) {
+ //---update-begin-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------
+ return ExcelUtil.remove0Suffix(result);
+ //---update-end-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------
+ }
+ // double类型防止科学计数法
+ if (result instanceof Double) {
+ return PoiPublicUtil.doubleToString((Double) result);
+ }
+ //---update-begin-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------
+ return ExcelUtil.remove0Suffix(String.valueOf(result));
+ //---update-end-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------
+ }
+ return result;
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ throw new ExcelImportException(ExcelImportEnum.GET_VALUE_ERROR);
+ }
+ }
+
+ /**
+ * 调用处理接口处理值
+ *
+ * @param dataHanlder
+ * @param object
+ * @param result
+ * @param titleString
+ * @return
+ */
+ private Object hanlderValue(IExcelDataHandler dataHanlder, Object object, Object result, String titleString) {
+ if (dataHanlder == null || dataHanlder.getNeedHandlerFields() == null || dataHanlder.getNeedHandlerFields().length == 0) {
+ return result;
+ }
+ if (hanlderList == null) {
+ hanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());
+ }
+ if (hanlderList.contains(titleString)) {
+ return dataHanlder.importHandler(object, titleString, result);
+ }
+ return result;
+ }
+
+ //update-begin-author:taoyan date:20180807 for:导入多值替换--
+ /**
+ * 导入支持多值替换
+ * @param replace 数据库中字典查询出来的数组
+ * @param result excel单元格获取的值
+ * @param multiReplace 是否支持多值替换
+ * @author taoYan
+ * @since 2018年8月7日
+ */
+ private Object replaceValue(String[] replace, Object result,boolean multiReplace) {
+ if(result == null){
+ return "";
+ }
+ if(replace == null || replace.length<=0){
+ return result;
+ }
+ String temp = String.valueOf(result);
+ String backValue = "";
+ if(temp.indexOf(",")>0 && multiReplace){
+ //原值中带有逗号,认为他是多值的
+ String multiReplaces[] = temp.split(",");
+ for (String str : multiReplaces) {
+ backValue = backValue.concat(replaceSingleValue(replace, str)+",");
+ }
+ if(backValue.equals("")){
+ backValue = temp;
+ }else{
+ backValue = backValue.substring(0, backValue.length()-1);
+ }
+ }else{
+ backValue = replaceSingleValue(replace, temp);
+ }
+ //update-begin-author:liusq date:20210204 for:字典替换失败提示日志
+ if(replace.length>0 && backValue.equals(temp)){
+ LOGGER.warn("====================字典替换失败,字典值:{},要转换的导入值:{}====================", replace, temp);
+ }
+ //update-end-author:liusq date:20210204 for:字典替换失败提示日志
+ return backValue;
+ }
+ /**
+ * 单值替换 ,若没找到则原值返回
+ */
+ private String replaceSingleValue(String[] replace, String temp){
+ String[] tempArr;
+ for (int i = 0; i < replace.length; i++) {
+ //update-begin---author:scott Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析---
+ //tempArr = replace[i].split("_");
+ tempArr = getValueArr(replace[i]);
+ if (temp.equals(tempArr[0]) || temp.replace("_","---").equals(tempArr[0])) {
+ //update-begin---author:wangshuai ---date:20220422 for:导入字典替换需要将---替换成_,不然数据库会存--- ------------
+ if(tempArr[1].contains("---")){
+ return tempArr[1].replace("---","_");
+ }
+ //update-end---author:wangshuai ---date:20220422 for:导入字典替换需要将---替换成_,不然数据库会存--- --------------
+ return tempArr[1];
+ }
+ //update-end---author:scott Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析---
+ }
+ return temp;
+ }
+ //update-end-author:taoyan date:20180807 for:导入多值替换--
+
+ /**
+ * 字典文本中含多个下划线横岗,取最后一个(解决空值情况)
+ *
+ * @param val
+ * @return
+ */
+ public String[] getValueArr(String val) {
+ int i = val.lastIndexOf("_");//最后一个分隔符的位置
+ String[] c = new String[2];
+ c[0] = val.substring(0, i); //label
+ c[1] = val.substring(i + 1); //key
+ return c;
+ }
+
+}
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java
new file mode 100644
index 0000000..7bdb7a7
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java
@@ -0,0 +1,620 @@
+/**
+ * Copyright 2013-2015 JEECG (jeecgos@163.com)
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 org.jeecgframework.poi.excel.imports;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.poifs.filesystem.FileMagic;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.formula.functions.T;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.jeecgframework.core.util.ApplicationContextUtil;
+import org.jeecgframework.poi.excel.annotation.ExcelTarget;
+import org.jeecgframework.poi.excel.entity.ImportParams;
+import org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams;
+import org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;
+import org.jeecgframework.poi.excel.entity.result.ExcelImportResult;
+import org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult;
+import org.jeecgframework.poi.excel.imports.base.ImportBaseService;
+import org.jeecgframework.poi.excel.imports.base.ImportFileServiceI;
+import org.jeecgframework.poi.excel.imports.verifys.VerifyHandlerServer;
+import org.jeecgframework.poi.exception.excel.ExcelImportException;
+import org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum;
+import org.jeecgframework.poi.util.ExcelUtil;
+import org.jeecgframework.poi.util.PoiPublicUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Excel 导入服务
+ *
+ * @author JEECG
+ * @date 2014年6月26日 下午9:20:51
+ */
+@SuppressWarnings({ "rawtypes", "unchecked", "hiding" })
+public class ExcelImportServer extends ImportBaseService {
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(ExcelImportServer.class);
+
+ private CellValueServer cellValueServer;
+
+ private VerifyHandlerServer verifyHandlerServer;
+
+ private boolean verfiyFail = false;
+ //仅允许字母数字字符的正则表达式
+ private static final Pattern lettersAndNumbersPattern = Pattern.compile("^[a-zA-Z0-9]+$") ;
+ /**
+ * 异常数据styler
+ */
+ private CellStyle errorCellStyle;
+
+ public ExcelImportServer() {
+ this.cellValueServer = new CellValueServer();
+ this.verifyHandlerServer = new VerifyHandlerServer();
+ }
+
+ /***
+ * 向List里面继续添加元素
+ *
+ * @param object
+ * @param param
+ * @param row
+ * @param titlemap
+ * @param targetId
+ * @param pictures
+ * @param params
+ */
+ private void addListContinue(Object object, ExcelCollectionParams param, Row row, Map titlemap, String targetId, Map pictures, ImportParams params) throws Exception {
+ Collection collection = (Collection) PoiPublicUtil.getMethod(param.getName(), object.getClass()).invoke(object, new Object[] {});
+ Object entity = PoiPublicUtil.createObject(param.getType(), targetId);
+ String picId;
+ boolean isUsed = false;// 是否需要加上这个对象
+ for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
+ Cell cell = row.getCell(i);
+ String titleString = (String) titlemap.get(i);
+ if (param.getExcelParams().containsKey(titleString)) {
+ if (param.getExcelParams().get(titleString).getType() == 2) {
+ picId = row.getRowNum() + "_" + i;
+ saveImage(object, picId, param.getExcelParams(), titleString, pictures, params);
+ } else {
+ saveFieldValue(params, entity, cell, param.getExcelParams(), titleString, row);
+ }
+ isUsed = true;
+ }
+ }
+ if (isUsed) {
+ collection.add(entity);
+ }
+ }
+
+ /**
+ * 获取key的值,针对不同类型获取不同的值
+ *
+ * @Author JEECG
+ * @date 2013-11-21
+ * @param cell
+ * @return
+ */
+ private String getKeyValue(Cell cell) {
+ if(cell==null){
+ return null;
+ }
+ Object obj = null;
+ switch (cell.getCellType()) {
+ case STRING:
+ obj = cell.getStringCellValue();
+ break;
+ case BOOLEAN:
+ obj = cell.getBooleanCellValue();
+ break;
+ case NUMERIC:
+ obj = cell.getNumericCellValue();
+ break;
+ case FORMULA:
+ obj = cell.getCellFormula();
+ break;
+ }
+ return obj == null ? null : obj.toString().trim();
+ }
+
+ /**
+ * 获取保存的真实路径
+ *
+ * @param excelImportEntity
+ * @param object
+ * @return
+ * @throws Exception
+ */
+ private String getSaveUrl(ExcelImportEntity excelImportEntity, Object object) throws Exception {
+ String url = "";
+ if (excelImportEntity.getSaveUrl().equals("upload")) {
+ 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() + "/" + url.substring(0, url.lastIndexOf("Entity"));
+ }
+ return excelImportEntity.getSaveUrl();
+ }
+ //update-begin--Author:xuelin Date:20171205 for:TASK #2098 【excel问题】 Online 一对多导入失败--------------------
+ private List importExcel(Collection result, Sheet sheet, Class> pojoClass, ImportParams params, Map pictures) throws Exception {
+ List collection = new ArrayList();
+ Map excelParams = new HashMap();
+ List excelCollection = new ArrayList();
+ String targetId = null;
+ if (!Map.class.equals(pojoClass)) {
+ Field fileds[] = PoiPublicUtil.getClassFields(pojoClass);
+ ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);
+ if (etarget != null) {
+ targetId = etarget.value();
+ }
+ getAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null);
+ }
+ ignoreHeaderHandler(excelParams, params);
+ Iterator rows = sheet.rowIterator();
+ Map titlemap = getTitleMap(sheet, rows, params, excelCollection);
+ //update-begin-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex
+ Set keys = excelParams.keySet();
+ for (String key : keys) {
+ if (key.startsWith("FIXED_")) {
+ String[] arr = key.split("_");
+ titlemap.put(Integer.parseInt(arr[1]), key);
+ }
+ }
+ //update-end-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex
+ Set columnIndexSet = titlemap.keySet();
+ Integer maxColumnIndex = Collections.max(columnIndexSet);
+ Integer minColumnIndex = Collections.min(columnIndexSet);
+ Row row = null;
+ //跳过表头和标题行
+ for (int j = 0; j < params.getTitleRows() + params.getHeadRows(); j++) {
+ row = rows.next();
+ }
+ Object object = null;
+ String picId;
+ while (rows.hasNext() && (row == null || sheet.getLastRowNum() - row.getRowNum() > params.getLastOfInvalidRow())) {
+ row = rows.next();
+ //update-begin--Author:xuelin Date:20171017 for:TASK #2373 【bug】表改造问题,导致 3.7.1批量导入用户bug-导入不成功--------------------
+ // 判断是集合元素还是不是集合元素,如果是就继续加入这个集合,不是就创建新的对象
+ //update-begin--Author:xuelin Date:20171206 for:TASK #2451 【excel导出bug】online 一对多导入成功, 但是现在代码生成后的一对多online导入有问题了
+ Cell keyIndexCell = row.getCell(params.getKeyIndex());
+ if (excelCollection.size()>0 && StringUtils.isEmpty(getKeyValue(keyIndexCell)) && object != null && !Map.class.equals(pojoClass)) {
+ //update-end--Author:xuelin Date:20171206 for:TASK #2451 【excel导出bug】online 一对多导入成功, 但是现在代码生成后的一对多online导入有问题了
+ for (ExcelCollectionParams param : excelCollection) {
+ addListContinue(object, param, row, titlemap, targetId, pictures, params);
+ }
+
+ } else {
+ object = PoiPublicUtil.createObject(pojoClass, targetId);
+ try {
+ //update-begin-author:taoyan date:20200303 for:导入图片
+ int firstCellNum = row.getFirstCellNum();
+ if(firstCellNum>minColumnIndex){
+ firstCellNum = minColumnIndex;
+ }
+ int lastCellNum = row.getLastCellNum();
+ if(lastCellNum excelParams,ImportParams params){
+ List ignoreList = new ArrayList<>();
+ for(String key:excelParams.keySet()){
+ String temp = excelParams.get(key).getGroupName();
+ if(temp!=null && temp.length()>0){
+ ignoreList.add(temp);
+ }
+ }
+ params.setIgnoreHeaderList(ignoreList);
+ }
+
+ /**
+ * 获取表格字段列名对应信息
+ *
+ * @param rows
+ * @param params
+ * @param excelCollection
+ * @return
+ */
+ private Map getTitleMap(Sheet sheet, Iterator rows, ImportParams params, List excelCollection) throws Exception {
+ Map titlemap = new HashMap();
+ Iterator| cellTitle = null;
+ String collectionName = null;
+ ExcelCollectionParams collectionParams = null;
+ Row headRow = null;
+ int headBegin = params.getTitleRows();
+ //update_begin-author:taoyan date:2020622 for:当文件行数小于代码里设置的TitleRows时headRow一直为空就会出现死循环
+ int allRowNum = sheet.getPhysicalNumberOfRows();
+ //找到首行表头,每个sheet都必须至少有一行表头
+ while(headRow == null && headBegin < allRowNum){
+ headRow = sheet.getRow(headBegin++);
+ }
+ if(headRow==null){
+ throw new Exception("不识别该文件");
+ }
+ //update-end-author:taoyan date:2020622 for:当文件行数小于代码里设置的TitleRows时headRow一直为空就会出现死循环
+
+ //设置表头行数
+ if (ExcelUtil.isMergedRegion(sheet, headRow.getRowNum(), 0)) {
+ params.setHeadRows(2);
+ }else{
+ params.setHeadRows(1);
+ }
+ cellTitle = headRow.cellIterator();
+ while (cellTitle.hasNext()) {
+ Cell cell = cellTitle.next();
+ String value = getKeyValue(cell);
+ if (StringUtils.isNotEmpty(value)) {
+ titlemap.put(cell.getColumnIndex(), value);//加入表头列表
+ }
+ }
+
+ //多行表头
+ for (int j = headBegin; j < headBegin + params.getHeadRows()-1; j++) {
+ headRow = sheet.getRow(j);
+ cellTitle = headRow.cellIterator();
+ while (cellTitle.hasNext()) {
+ Cell cell = cellTitle.next();
+ String value = getKeyValue(cell);
+ if (StringUtils.isNotEmpty(value)) {
+ int columnIndex = cell.getColumnIndex();
+ //当前cell的上一行是否为合并单元格
+ if(ExcelUtil.isMergedRegion(sheet, cell.getRowIndex()-1, columnIndex)){
+ collectionName = ExcelUtil.getMergedRegionValue(sheet, cell.getRowIndex()-1, columnIndex);
+ if(params.isIgnoreHeader(collectionName)){
+ titlemap.put(cell.getColumnIndex(), value);
+ }else{
+ titlemap.put(cell.getColumnIndex(), collectionName + "_" + value);
+ }
+ }else{
+ //update-begin-author:taoyan date:20220112 for: JT640 【online】导入 无论一对一还是一对多 如果子表只有一个字段 则子表无数据
+ // 上一行不是合并的情况下另有一种特殊的场景: 如果当前单元格和上面的单元格同一列 即子表字段只有一个 所以标题没有出现跨列
+ String prefixTitle = titlemap.get(cell.getColumnIndex());
+ if(prefixTitle!=null && !"".equals(prefixTitle)){
+ titlemap.put(cell.getColumnIndex(), prefixTitle + "_" +value);
+ }else{
+ titlemap.put(cell.getColumnIndex(), value);
+ }
+ //update-end-author:taoyan date:20220112 for: JT640 【online】导入 无论一对一还是一对多 如果子表只有一个字段 则子表无数据
+ }
+ /*int i = cell.getColumnIndex();
+ // 用以支持重名导入
+ if (titlemap.containsKey(i)) {
+ collectionName = titlemap.get(i);
+ collectionParams = getCollectionParams(excelCollection, collectionName);
+ titlemap.put(i, collectionName + "_" + value);
+ } else if (StringUtils.isNotEmpty(collectionName) && collectionParams.getExcelParams().containsKey(collectionName + "_" + value)) {
+ titlemap.put(i, collectionName + "_" + value);
+ } else {
+ collectionName = null;
+ collectionParams = null;
+ }
+ if (StringUtils.isEmpty(collectionName)) {
+ titlemap.put(i, value);
+ }*/
+ }
+ }
+ }
+ return titlemap;
+ }
+ //update-end--Author:xuelin Date:20171205 for:TASK #2098 【excel问题】 Online 一对多导入失败--------------------
+ /**
+ * 获取这个名称对应的集合信息
+ *
+ * @param excelCollection
+ * @param collectionName
+ * @return
+ */
+ private ExcelCollectionParams getCollectionParams(List excelCollection, String collectionName) {
+ for (ExcelCollectionParams excelCollectionParams : excelCollection) {
+ if (collectionName.equals(excelCollectionParams.getExcelName())) {
+ return excelCollectionParams;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Excel 导入 field 字段类型 Integer,Long,Double,Date,String,Boolean
+ *
+ * @param inputstream
+ * @param pojoClass
+ * @param params
+ * @return
+ * @throws Exception
+ */
+ public ExcelImportResult importExcelByIs(InputStream inputstream, Class> pojoClass, ImportParams params) throws Exception {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Excel import start ,class is {}", pojoClass);
+ }
+ List result = new ArrayList();
+ Workbook book = null;
+ boolean isXSSFWorkbook = false;
+ if (!(inputstream.markSupported())) {
+ inputstream = new PushbackInputStream(inputstream, 8);
+ }
+ //begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------
+ //------poi4.x begin----
+// FileMagic fm = FileMagic.valueOf(FileMagic.prepareToCheckMagic(inputstream));
+// if(FileMagic.OLE2 == fm){
+// isXSSFWorkbook=false;
+// }
+ book = WorkbookFactory.create(inputstream);
+ if(book instanceof XSSFWorkbook){
+ isXSSFWorkbook=true;
+ }
+ LOGGER.info(" >>> poi3升级到4.0兼容改造工作, isXSSFWorkbook = " +isXSSFWorkbook);
+ //end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------
+
+ //begin-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------
+ //获取导入文本的sheet数
+ //update-begin-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
+ if(params.getSheetNum()==0){
+ int sheetNum = book.getNumberOfSheets();
+ if(sheetNum>0){
+ params.setSheetNum(sheetNum);
+ }
+ }
+ //update-end-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
+ //end-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------
+ createErrorCellStyle(book);
+ Map pictures;
+ // 获取指定的sheet名称
+ String sheetName = params.getSheetName();
+
+ //update-begin-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数
+ for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()
+ + params.getSheetNum(); i++) {
+ //update-end-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数
+
+ //update-begin-author:taoyan date:2023-3-4 for: 导入数据支持指定sheet名称
+ if(sheetName!=null && !"".equals(sheetName)){
+ Sheet tempSheet = book.getSheetAt(i);
+ if(!sheetName.equals(tempSheet.getSheetName())){
+ continue;
+ }
+ }
+ //update-end-author:taoyan date:2023-3-4 for: 导入数据支持指定sheet名称
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(" start to read excel by is ,startTime is {}", System.currentTimeMillis());
+ }
+ 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().getTime());
+ }
+ result.addAll(importExcel(result, book.getSheetAt(i), pojoClass, params, pictures));
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(" end to read excel list by pos ,endTime is {}", new Date().getTime());
+ }
+ }
+ if (params.isNeedSave()) {
+ saveThisExcel(params, pojoClass, isXSSFWorkbook, book);
+ }
+ return new ExcelImportResult(result, verfiyFail, book);
+ }
+ /**
+ *
+ * @param is
+ * @return
+ * @throws IOException
+ */
+ public static byte[] getBytes(InputStream is) throws IOException {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ int len;
+ byte[] data = new byte[100000];
+ while ((len = is.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, len);
+ }
+
+ buffer.flush();
+ return buffer.toByteArray();
+ }
+
+ /**
+ * 保存字段值(获取值,校验值,追加错误信息)
+ *
+ * @param params
+ * @param object
+ * @param cell
+ * @param excelParams
+ * @param titleString
+ * @param row
+ * @throws Exception
+ */
+ private void saveFieldValue(ImportParams params, Object object, Cell cell, Map excelParams, String titleString, Row row) throws Exception {
+ Object value = cellValueServer.getValue(params.getDataHanlder(), object, cell, excelParams, titleString);
+ if (object instanceof Map) {
+ if (params.getDataHanlder() != null) {
+ params.getDataHanlder().setMapValue((Map) object, titleString, value);
+ } else {
+ ((Map) object).put(titleString, value);
+ }
+ } else {
+ ExcelVerifyHanlderResult verifyResult = verifyHandlerServer.verifyData(object, value, titleString, excelParams.get(titleString).getVerify(), params.getVerifyHanlder());
+ if (verifyResult.isSuccess()) {
+ setValues(excelParams.get(titleString), object, value);
+ } else {
+ Cell errorCell = row.createCell(row.getLastCellNum());
+ errorCell.setCellValue(verifyResult.getMsg());
+ errorCell.setCellStyle(errorCellStyle);
+ verfiyFail = true;
+ throw new ExcelImportException(ExcelImportEnum.VERIFY_ERROR);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param object
+ * @param picId
+ * @param excelParams
+ * @param titleString
+ * @param pictures
+ * @param params
+ * @throws Exception
+ */
+ private void saveImage(Object object, String picId, Map excelParams, String titleString, Map pictures, ImportParams params) throws Exception {
+ if (pictures == null || pictures.get(picId)==null) {
+ return;
+ }
+ PictureData image = pictures.get(picId);
+ byte[] data = image.getData();
+ String fileName = "pic" + Math.round(Math.random() * 100000000000L);
+ fileName += "." + PoiPublicUtil.getFileExtendName(data);
+ //update-beign-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159
+ int saveType = excelParams.get(titleString).getSaveType();
+ if ( saveType == 1) {
+ String path = PoiPublicUtil.getWebRootPath(getSaveUrl(excelParams.get(titleString), object));
+ File savefile = new File(path);
+ if (!savefile.exists()) {
+ savefile.mkdirs();
+ }
+ savefile = new File(path + "/" + fileName);
+ FileOutputStream fos = new FileOutputStream(savefile);
+ fos.write(data);
+ fos.close();
+ setValues(excelParams.get(titleString), object, getSaveUrl(excelParams.get(titleString), object) + "/" + fileName);
+ } else if(saveType==2) {
+ setValues(excelParams.get(titleString), object, data);
+ } else {
+ ImportFileServiceI importFileService = null;
+ try {
+ importFileService = ApplicationContextUtil.getContext().getBean(ImportFileServiceI.class);
+ } catch (Exception e) {
+ System.err.println(e.getMessage());
+ }
+ if(importFileService!=null){
+ //update-beign-author:liusq date:20230411 for:【issue/4415】autopoi-web 导入图片字段时无法指定保存路径
+ String saveUrl = excelParams.get(titleString).getSaveUrl();
+ String dbPath;
+ if(StringUtils.isNotBlank(saveUrl)){
+ LOGGER.debug("图片保存路径saveUrl = "+saveUrl);
+ Matcher matcher = lettersAndNumbersPattern.matcher(saveUrl);
+ if(!matcher.matches()){
+ LOGGER.warn("图片保存路径格式错误,只能设置字母和数字的组合!");
+ dbPath = importFileService.doUpload(data);
+ }else{
+ dbPath = importFileService.doUpload(data,saveUrl);
+ }
+ }else{
+ dbPath = importFileService.doUpload(data);
+ }
+ //update-end-author:liusq date:20230411 for:【issue/4415】autopoi-web 导入图片字段时无法指定保存路径
+ setValues(excelParams.get(titleString), object, dbPath);
+ }
+ }
+ //update-end-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159
+ }
+
+ private void createErrorCellStyle(Workbook workbook) {
+ errorCellStyle = workbook.createCellStyle();
+ Font font = workbook.createFont();
+ font.setColor(Font.COLOR_RED);
+ errorCellStyle.setFont(font);
+ }
+
+}
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SaxReadExcel.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SaxReadExcel.java
new file mode 100644
index 0000000..3f16c69
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SaxReadExcel.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright 2013-2015 JEECG (jeecgos@163.com)
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 org.jeecgframework.poi.excel.imports.sax;
+
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.xssf.eventusermodel.XSSFReader;
+import org.apache.poi.xssf.model.SharedStringsTable;
+import org.jeecgframework.poi.excel.entity.ImportParams;
+import org.jeecgframework.poi.excel.imports.sax.parse.ISaxRowRead;
+import org.jeecgframework.poi.excel.imports.sax.parse.SaxRowRead;
+import org.jeecgframework.poi.exception.excel.ExcelImportException;
+import org.jeecgframework.poi.handler.inter.IExcelReadRowHanlder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * 基于SAX Excel大数据读取,读取Excel 07版本,不支持图片读取
+ *
+ * @author JEECG
+ * @date 2014年12月29日 下午9:41:38
+ * @version 1.0
+ */
+@SuppressWarnings("rawtypes")
+public class SaxReadExcel {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SaxReadExcel.class);
+
+ public List readExcel(InputStream inputstream, Class> pojoClass, ImportParams params, ISaxRowRead rowRead, IExcelReadRowHanlder hanlder) {
+ try {
+ OPCPackage opcPackage = OPCPackage.open(inputstream);
+ return readExcel(opcPackage, pojoClass, params, rowRead, hanlder);
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ throw new ExcelImportException(e.getMessage());
+ }
+ }
+
+ private List readExcel(OPCPackage opcPackage, Class> pojoClass, ImportParams params, ISaxRowRead rowRead, IExcelReadRowHanlder hanlder) {
+ try {
+ XSSFReader xssfReader = new XSSFReader(opcPackage);
+ SharedStringsTable sst = (SharedStringsTable) xssfReader.getSharedStringsTable();
+ if (rowRead == null) {
+ rowRead = new SaxRowRead(pojoClass, params, hanlder);
+ }
+ XMLReader parser = fetchSheetParser(sst, rowRead);
+ Iterator sheets = xssfReader.getSheetsData();
+ int sheetIndex = 0;
+ while (sheets.hasNext() && sheetIndex < params.getSheetNum()) {
+ sheetIndex++;
+ InputStream sheet = sheets.next();
+ InputSource sheetSource = new InputSource(sheet);
+ parser.parse(sheetSource);
+ sheet.close();
+ }
+ return rowRead.getList();
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ throw new ExcelImportException("SAX导入数据失败");
+ }
+ }
+
+ private XMLReader fetchSheetParser(SharedStringsTable sst, ISaxRowRead rowRead) throws SAXException {
+ XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
+ ContentHandler handler = new SheetHandler(sst, rowRead);
+ parser.setContentHandler(handler);
+ return parser;
+ }
+
+}
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SheetHandler.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SheetHandler.java
new file mode 100644
index 0000000..893a99e
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SheetHandler.java
@@ -0,0 +1,136 @@
+/**
+ * Copyright 2013-2015 JEECG (jeecgos@163.com)
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 org.jeecgframework.poi.excel.imports.sax;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.xssf.model.SharedStringsTable;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.jeecgframework.poi.excel.entity.enmus.CellValueType;
+import org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity;
+import org.jeecgframework.poi.excel.imports.sax.parse.ISaxRowRead;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.google.common.collect.Lists;
+
+/**
+ * 回调接口
+ *
+ * @author JEECG
+ * @date 2014年12月29日 下午9:50:09
+ */
+public class SheetHandler extends DefaultHandler {
+
+ private SharedStringsTable sst;
+ private String lastContents;
+
+ // 当前行
+ private int curRow = 0;
+ // 当前列
+ private int curCol = 0;
+
+ private CellValueType type;
+
+ private ISaxRowRead read;
+
+ // 存储行记录的容器
+ private List rowlist = Lists.newArrayList();
+
+ public SheetHandler(SharedStringsTable sst, ISaxRowRead rowRead) {
+ this.sst = sst;
+ this.read = rowRead;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
+ // 置空
+ lastContents = "";
+ // c => 单元格
+ if ("c".equals(name)) {
+ // 如果下一个元素是 SST 的索引,则将nextIsString标记为true
+ String cellType = attributes.getValue("t");
+ if ("s".equals(cellType)) {
+ type = CellValueType.String;
+ return;
+ }
+ // 日期格式
+ cellType = attributes.getValue("s");
+ if ("1".equals(cellType)) {
+ type = CellValueType.Date;
+ } else if ("2".equals(cellType)) {
+ type = CellValueType.Number;
+ }
+ } else if ("t".equals(name)) {// 当元素为t时
+ type = CellValueType.TElement;
+ }
+
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String name) throws SAXException {
+
+ // 根据SST的索引值的到单元格的真正要存储的字符串
+ // 这时characters()方法可能会被调用多次
+ if (CellValueType.String.equals(type)) {
+ try {
+ int idx = Integer.parseInt(lastContents);
+ lastContents = sst.getItemAt(idx).getString();
+ } catch (Exception e) {
+
+ }
+ }
+ // t元素也包含字符串
+ if (CellValueType.TElement.equals(type)) {
+ String value = lastContents.trim();
+ rowlist.add(curCol, new SaxReadCellEntity(CellValueType.String, value));
+ curCol++;
+ type = CellValueType.None;
+ // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
+ // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
+ } else if ("v".equals(name)) {
+ String value = lastContents.trim();
+ value = value.equals("") ? " " : value;
+ if (CellValueType.Date.equals(type)) {
+ Date date = DateUtil.getJavaDate(Double.valueOf(value));
+ rowlist.add(curCol, new SaxReadCellEntity(CellValueType.Date, date));
+ } else if (CellValueType.Number.equals(type)) {
+ BigDecimal bd = new BigDecimal(value);
+ rowlist.add(curCol, new SaxReadCellEntity(CellValueType.Number, bd));
+ } else if (CellValueType.String.equals(type)) {
+ rowlist.add(curCol, new SaxReadCellEntity(CellValueType.String, value));
+ }
+ curCol++;
+ } else if (name.equals("row")) {// 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法
+ read.parse(curRow, rowlist);
+ rowlist.clear();
+ curRow++;
+ curCol = 0;
+ }
+
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ // 得到单元格内容的值
+ lastContents += new String(ch, start, length);
+ }
+
+}
diff --git a/autopoi/autopoi/src/main/java/org/jeecgframework/poi/util/ExcelUtil.java b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/util/ExcelUtil.java
new file mode 100644
index 0000000..49826f2
--- /dev/null
+++ b/autopoi/autopoi/src/main/java/org/jeecgframework/poi/util/ExcelUtil.java
@@ -0,0 +1,327 @@
+package org.jeecgframework.poi.util;
+/**
+ * @author Link Xue
+ * @version 20171025
+ * POI对EXCEL操作工具
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+public class ExcelUtil {
+
+ public static void main(String[] args){
+ //读取excel数据
+ ArrayList | | |