Java导入导出Excel工具类

Java导入导出Excel工具类

这是一个工具类,用于Java语言的Excel表单导入导出。该工具的原始版本是若依框架的,因原版本不满足业务需求,修改而来,在此备份一下。若使用时有自己业务没有的依赖自己酌情修改。
贴原代码~~~~

package com.xunmeng.common.core.utils.poi;

import com.xunmeng.common.core.annotation.Excel;
import com.xunmeng.common.core.annotation.Excel.ColumnType;
import com.xunmeng.common.core.annotation.Excel.Type;
import com.xunmeng.common.core.annotation.Excels;
import com.xunmeng.common.core.text.Convert;
import com.xunmeng.common.core.utils.DateUtils;
import com.xunmeng.common.core.utils.StringUtils;
import com.xunmeng.common.core.utils.file.FileTypeUtils;
import com.xunmeng.common.core.utils.file.ImageUtils;
import com.xunmeng.common.core.utils.reflect.ReflectUtils;
import com.xunmeng.common.core.web.domain.BaseEntity;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.ss.util.RegionUtil;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;


/**
 * Excel相关处理 包含导入导出
 * 数据为 List<LinkedHashMap<String,Object>> 格式,灵活型。(适用于没有实例对象的动态数据导入导出)
 *
 * @author xunmeng
 */
public class ExcelUtil1
{
    /**
     * 日志对象
     */
    private static final Logger log = LoggerFactory.getLogger(ExcelUtil1.class);

    /**
     * 字符串防注入检查正则
     */
    private static final String FORMULA_REGEX_STR = "=|-|\\+|@";

    /**
     * 字符串防注入检查正则
     */
    private static final String[] FORMULA_STR = { "=", "-", "+", "@" };

    /**
     * Excel sheet最大行数,默认65536
     */
    private static final int sheetSize = 65536;

    /**
     * 导出文件的名称
     */
    private String fileName;

    /**
     * 工作表sheet的名称
     */
    private String sheetName;

    /**
     * 多个工作表,sheet的名称
     */
    private List<String> sheetNameList;

    /**
     * 处理类型(EXPORT:导出数据;IMPORT:导入模板)
     */
    private Type type; //Type是一个枚举

    /**
     * 工作薄对象
     */
    private Workbook wb;

    /**
     * 当前操作工作表对象
     */
    private Sheet sheet;

    /**
     * 样式列表
     */
    private Map<String, CellStyle> styles;

    /**
     * 导入导出数据列表
     */
    private List<LinkedHashMap<String,Object>> list = new ArrayList<>();

    /**
     * 多个工作表的数据
     */
    private List<List<LinkedHashMap<String,Object>>> muchList = new ArrayList<>();

    /**
     * 导入导出数据对象列表
     */
    private List<? extends BaseEntity> objectList = new ArrayList<>();

    /**
     * 多个工作表对象数据
     */
    private List<List<? extends BaseEntity>> muchObjectList = new ArrayList<>();

    /**
     * 表头列表和单元格样式
     */
    private List<LinkedHashMap<String,Object>> fields;

    /**
     * 多个工作表的表头列表和单元格样式
     */
    private List<List<LinkedHashMap<String, Object>>> fieldsList;

    /**
     * 当前行号
     */
    private int rowNum;

    /**
     * 标题
     */
    private String title;

    /**
     * 个工作表的标题
     */
    private List<String> titleList;

    /**
     * 最大高度
     */
    private short maxHeight;

    /**
     * 统计列表
     */
    private Map<Integer, Double> statistics = new HashMap<>();

    /**
     * 当前sheet索引
     */
    private int sheetIndex;

    /**
     * 数字格式
     */
    private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");

    /**
     * 注解列表
     */
    private List<Object[]> excelFields;

    /**
     * 多个实体注解列表
     */
    private List<List<Object[]>> excelFieldsList;

    /**
     * 注解的字段中文名称与字段的属性名称对应关系
     */
    private HashMap<String,Field> fieldJoinAttr;

    /**
     * 多个实体类
     * 注解的字段中文名称与字段的属性名称对应关系
     */
    private List<HashMap<String,Field>> fieldJoinAttrList;

    /**
     * 注解的字段中文名称与字段注解对应关系
     */
    private HashMap<String,Excel> fieldJoinExcel;

    /**
     * 多个实体类
     * 注解的字段中文名称与字段注解对应关系
     */
    private List<HashMap<String,Excel>> fieldJoinExcelList;

    /**
     * 控制操作是直接导出下载还是返回工作表自己处理
     * 1直接输出下载,2保存成文件到指定路径,3转换成MultipartFile
     */
    private int exportOrSave = 1;

    /**
     * Workbook 转 multipartFile
     */
    private MultipartFile multipartFile;

    /**
     * 控制在操作处理完是否自动关闭Workbook,如果不自动关闭需要自己处理玩法调用关闭方法
     */
    private boolean isAutoCloseWorkbook = true;

    /**
     * 需要排除列属性
     */
    public String[] excludeFields;

    /**
     * 需要排除列属性,根据类名
     */
    public HashMap<String,String[]> excludeFieldsByClassName = new HashMap<>();

    /**
     * 需要排除列属性的注解名称
     */
    public String[] excludeExcelName;

    /**
     * 存放excel存在的表头和实体类属性的对应
     */
    public List<HashMap<String,String>> excelHasHead = new ArrayList<>();

    /**
     * 存放excel存在的表头和实体类属性的对应
     */
    public HashMap<String,List<HashMap<String,String>>> excelHasHeadList = new HashMap<>();

    /*----------------------------导入数据------------------------------------*/
    /**
     * 把excel表单转换成List<LinkedHashMap<String,Object>>
     * 只有文件,其他默认
     * @param is 输入流(上传的Excel文件的二进制流)
     * @return 转换后集合
     */
    public List<LinkedHashMap<String,Object>> importExcel(InputStream is) throws Exception
    {
        return importExcel(is, 0);
    }

    /**
     * 把excel表单转换成List<LinkedHashMap<String,Object>>
     * 指定标题所在行
     * @param is 输入流(上传的Excel文件)
     * @param titleNum 标题占用行数
     * @return 转换后集合
     */
    public List<LinkedHashMap<String,Object>> importExcel(InputStream is, int titleNum) throws Exception
    {
        return importExcel( is, null,StringUtils.EMPTY,titleNum);
    }

    /**
     * 把excel表单转换成List<LinkedHashMap<String,Object>>
     * 指定获取的列
     * @param is 输入流(上传的Excel文件)
     * @param fields 指定要获取的列数据,及获取那些表头名称的数据,默认是全部
     * @return 转换后集合
     */
    public List<LinkedHashMap<String,Object>> importExcel(InputStream is,List<LinkedHashMap<String, Object>> fields) throws Exception
    {
        return importExcel(is, fields,StringUtils.EMPTY,0);
    }

    /**
     * 把excel表单转换成List<LinkedHashMap<String,Object>>
     * 指定获取的列,指定标题所在行
     * @param is 输入流(上传的Excel文件)
     * @param titleNum 标题占用行数
     * @return 转换后集合
     */
    public List<LinkedHashMap<String,Object>> importExcel(InputStream is, List<LinkedHashMap<String, Object>> fields,int titleNum) throws Exception
    {
        return importExcel( is,fields, StringUtils.EMPTY,titleNum);
    }

    /**
     * 把excel表单转换成List<LinkedHashMap<String,Object>>
     * @param sheetName 表格索引名
     * @param titleNum 标题占用行数
     * @param is 输入流(上传的Excel文件)
     * @param fields 指定要获取的列数据,及获取那些表头名称的数据,默认是全部
     * @return 转换后集合
     */
    public List<LinkedHashMap<String,Object>> importExcel( InputStream is,List<LinkedHashMap<String, Object>> fields, String sheetName,int titleNum) throws Exception
    {
        this.fields = fields;
        this.type = Type.IMPORT;//导入数据
        //把输入流丢给 WorkbookFactory.create 自动创建 workbook。不用判断文件是xls(2003)还是xlsx(2007)类型,create会自动判断
        this.wb = WorkbookFactory.create(is);
        readData(sheetName,titleNum,null);
        return list;
    }

    /**
     * 多工作表
     * 把excel表单转换成List<LinkedHashMap<String,Object>>
     * @param titleNumList 标题占用行数
     * @param is 输入流(上传的Excel文件)
     * @param fields 指定要获取的列数据,及获取那些表头名称的数据,默认是全部 ()
     * @return 转换后集合
     */
    public List<List<LinkedHashMap<String,Object>>> importExcelMuchWork(InputStream is,HashMap<String,List<LinkedHashMap<String, Object>>> fields, List<Integer> titleNumList) throws Exception
    {
        this.type = Type.IMPORT;//导入数据
        //把输入流丢给 WorkbookFactory.create 自动创建 workbook。不用判断文件是xls(2003)还是xlsx(2007)类型,create会自动判断
        this.wb = WorkbookFactory.create(is);
        int sheetNum = wb.getNumberOfSheets();
        for (int i = 0; i < sheetNum; i++) {
            this.list = new ArrayList<>();
            String sheetName = sheet.getSheetName();
            if(fields != null && fields.containsKey(sheetName)){
                this.fields = fields.get(sheetName);
            }else{
                this.fields = null;
            }
            sheet = wb.getSheetAt(i);
            int titleNum = titleNumList.get(i);
            readData(sheet.getSheetName(),titleNum,null);
            muchList.add(this.list);
        }
        return muchList;
    }

    /**
     * 把excel表单转换成对象列表
     * @param is 输入流(上传的Excel文件)
     * @param  clazz 指定的实体类
     * @return 转换后集合
     */
    public <T extends BaseEntity> List<T> importExcel(Class<T> clazz,InputStream is) throws Exception
    {
        return importExcel(clazz,is,StringUtils.EMPTY,0);
    }

    /**
     * 把excel表单转换成对象列表
     * @param sheetName 表格索引名
     * @param titleNum 标题占用行数
     * @param is 输入流(上传的Excel文件)
     * @param  clazz 指定的实体类
     * @return 转换后集合
     */
    public <T extends BaseEntity> List<T> importExcel(Class<T> clazz,InputStream is, String sheetName, int titleNum) throws Exception
    {
        this.type = Type.IMPORT;//导入数据
        this.setFieldsByReadClassAttr(clazz,null);
        //把输入流丢给 WorkbookFactory.create 自动创建 workbook。不用判断文件是xls(2003)还是xlsx(2007)类型,create会自动判断
        this.wb = WorkbookFactory.create(is);
        readData(sheetName,titleNum,clazz);
        return (List<T>) objectList;
    }

    /**
     * 多工作表
     * 把excel表单转换成对象列表
     * @param titleNumList 标题占用行数
     * @param is 输入流(上传的Excel文件)
     * @param sheetNameJoinClass 指定要读取的sheet和对应的实体类
     * @return 转换后集合
     */
    public List<List<? extends BaseEntity>> importExcelMuchWork(HashMap<String,Class<? extends BaseEntity>> sheetNameJoinClass,InputStream is, HashMap<String,Integer> titleNumList) throws Exception
    {
        this.type = Type.IMPORT;//导入数据
        //把输入流丢给 WorkbookFactory.create 自动创建 workbook。不用判断文件是xls(2003)还是xlsx(2007)类型,create会自动判断
        this.wb = WorkbookFactory.create(is);
        int sheetNum = wb.getNumberOfSheets();
        for (int i = 0; i < sheetNum; i++) {
            this.objectList = new ArrayList<>();
            this.excelHasHead = new ArrayList<>();
            sheet = wb.getSheetAt(i);
            String sheetName = sheet.getSheetName();
            if(sheetNameJoinClass.containsKey(sheetName)){
                Class<? extends BaseEntity> clazz = sheetNameJoinClass.get(sheetName);
                setFieldsByReadClassAttr(clazz,null);
                int titleNum = 0;
                if(titleNumList != null && titleNumList.containsKey(sheetName)){
                    titleNum = titleNumList.get(sheetName);
                }
                readData(sheetName,titleNum,clazz);

                muchObjectList.add(this.objectList);
                String className = StringUtils.toLowerCaseFirstOne(clazz.getSimpleName());
                int indexOf = className.indexOf("sys");
                if(indexOf == 0){
                    className = StringUtils.toLowerCaseFirstOne(StringUtils.substring(className, 3));
                }
                this.excelHasHeadList.put(className,this.excelHasHead);
            }
        }
        return muchObjectList;
    }

    /**
     * 读取数据
     * @param sheetName sheetName
     * @param titleNum 标题占用行数
     */
    private <T extends BaseEntity> void readData(String sheetName, int titleNum,Class<T> clazz) throws IOException, InstantiationException, IllegalAccessException {
        // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
        Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
        //判断是不是空表
        if (sheet == null) {
            throw new IOException("文件sheet不存在");
        }
        // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
        int rows = sheet.getLastRowNum();
        if (rows > 0) {
            // 定义一个map用于存放excel列的序号和field.
            Map<String, Integer> cellMap = new HashMap<>();
            // 获取表头
            Row heard = sheet.getRow(titleNum);
            //getPhysicalNumberOfCells(),这个是用来获取不为空的的列个数。
            //getLastCellNum是获取最后一个不为空的列是第几个
            for (int i = 0,cloNumber = heard.getPhysicalNumberOfCells(); i < cloNumber; i++)
            {
                Cell cell = heard.getCell(i);
                if (StringUtils.isNotNull(cell)) { //判断单元格是不是空的
                    String value = this.getCellValue(heard, i).toString();
                    cellMap.put(value, i);
                } else {
                    cellMap.put(null, i);
                }
            }
            //列序号和列名称
            Map<Integer, LinkedHashMap<String,Object>> fieldsMap = new HashMap<>();
            //如果指定获取的列,默认全部列
            if(this.fields != null){
                for (LinkedHashMap<String,Object> field : this.fields) {
                    Integer column = cellMap.get((String)field.get("name"));
                    if (column != null) {
                        //如果上传的文件中存在这一列
                        fieldsMap.put(column,field);
                        saveHeard((String)field.get("name"));
                    }
                }
            }else{
                for (Map.Entry<String, Integer> entry : cellMap.entrySet()) {
                    LinkedHashMap<String,Object> field = new LinkedHashMap<>();
                    field.put("name",entry.getKey());
                    fieldsMap.put(entry.getValue(),field);
                    saveHeard(entry.getKey());
                }
            }
            //取出工作表数据
            //从第标题行的下一行开始取数据
            List<T> entityList = new ArrayList<>();
            for (int i = titleNum + 1; i <= rows; i++) {
                Row row = sheet.getRow(i);
                // 判断当前行是否是空行
                if (isRowEmpty(row)) {
                    continue;
                }
                LinkedHashMap<String,Object> data = null;
                T entity = null;
                if(clazz != null){
                    entity = clazz.newInstance();//一行数据
                }else{
                    data = new LinkedHashMap<>();//一行数据
                }
                for (Map.Entry<Integer, LinkedHashMap<String,Object>> entry : fieldsMap.entrySet()) {
                    Object val = this.getCellValue(row, entry.getKey());
                    LinkedHashMap<String, Object> field = entry.getValue();

                    if(clazz != null){
                        Field field1 = this.fieldJoinAttr.get(field.get("name").toString());
                        Excel attr = this.fieldJoinExcel.get(field.get("name").toString());
                        setValToEntity(entity,field1,attr,val);
                    }else{
                        if(field.containsKey("readConverterExp") && StringUtils.isNotEmpty(field.get("readConverterExp").toString())){
                            String readConverterExp = field.get("readConverterExp").toString();
                            if(StringUtils.isNotEmpty(readConverterExp)){
                                String separator = ",";
                                if(field.containsKey("separator")){
                                    separator = field.get("separator").toString();
                                }
                                val = reverseByExp(Convert.toStr(val),field.get("readConverterExp").toString(),separator);
                            }
                        }
                        data.put((String) field.get("name"), val);
                    }
                }
                if(clazz != null){
                    entityList.add(entity);
                }else{
                    this.list.add(data);
                }
            }
            this.objectList = entityList;
        }
    }

    /**
     * 从一行获取指定单元格的值
     * @param row 获取的行
     * @param column 获取单元格列号
     * @return 单元格值
     */
    private Object getCellValue(Row row, int column)
    {
        if (row == null) {
            return row;
        }
        Object val = "";
        try {
            Cell cell = row.getCell(column);//获取单元格
            //判断是否为空
            if (StringUtils.isNotNull(cell)) {
                //判断单元格数据类型
                if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) {
                    //数字类型或公式类型,通过getNumericCellValue获取值
                    val = cell.getNumericCellValue();
                    //判断是否是时间格式
                    if (DateUtil.isCellDateFormatted(cell)) {
                        val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
                    } else {
                        //转换成数值对象
                        if ((Double)val % 1 != 0) {
                            val = new BigDecimal(val.toString());//小数
                        } else {
                            val = new DecimalFormat("0").format(val);//整数
                        }
                    }
                } else if (cell.getCellType() == CellType.STRING) {
                    //字符串类型
                    val = cell.getStringCellValue();
                } else if (cell.getCellType() == CellType.BOOLEAN) {
                    //布尔类型
                    val = cell.getBooleanCellValue();
                }else if (cell.getCellType() == CellType.ERROR) {
                    //错误类型
                    val = cell.getErrorCellValue();
                }
            }
        } catch (Exception e) {
            return val;
        }
        return val;
    }

    /**
     * 如果是实体类,自动转换陈实体类
     * @param entity 实体对象
     * @param field 当前属性字段
     * @param attr Excel注解信息
     * @param val 值
     */
    private <T> void setValToEntity(T entity,Field field,Excel attr,Object val){
        // 取得类型,并根据对象类型设置值.
        Class<?> fieldType = field.getType();
        if (String.class == fieldType) {
            //字符串类型
            String s = Convert.toStr(val);
            if (StringUtils.endsWith(s, ".0")) {
                // 如果结尾带.0把。0qu'diao
                val = StringUtils.substringBefore(s, ".0");
            } else {
                //如果注解转时间字符串
                if (StringUtils.isNotEmpty(attr.dateFormat())) {
                    val = parseDateToStr(attr.dateFormat(), val);
                } else {
                    val = Convert.toStr(val);
                }
            }
        } else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
            //整型并且值是数字格式
            val = Convert.toInt(val);
        } else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
            //大整型并且值是数字格式
            val = Convert.toLong(val);
        } else if (Double.TYPE == fieldType || Double.class == fieldType) {
            //Double类型
            val = Convert.toDouble(val);
        } else if (Float.TYPE == fieldType || Float.class == fieldType) {
            //Float类型
            val = Convert.toFloat(val);
        } else if (BigDecimal.class == fieldType) {
            //高精度大数类型
            val = Convert.toBigDecimal(val);
        } else if (Date.class == fieldType) {
            //日期类型
            if (val instanceof String) {
                //值是字符串,直接转日期
                val = DateUtils.parseDate(val);
            } else if (val instanceof Double) {
                //值是Double
                val = DateUtil.getJavaDate((Double) val);
            }
        } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
            //Boolean类型
            val = Convert.toBool(val, false);
        }
        //属性字段名称
        String propertyName = field.getName();
        if (StringUtils.isNotEmpty(attr.targetAttr())) {
            //如果是另一个类,拼接名称
            propertyName = field.getName() + "." + attr.targetAttr();
        } else if (StringUtils.isNotEmpty(attr.readConverterExp())) {
            //如果需要转换表达式
            val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
        } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
            //如果自定义数据处理器
            val = dataFormatHandlerAdapter(val, attr.handler(), attr.args());
        }
        //给对象设置值
        if(!StringUtils.isNull(val) && !StringUtils.isEmpty(val.toString())){
            ReflectUtils.invokeSetter(entity, propertyName, val);
        }
    }

    /*----------------------------导入数据方法完------------------------------------*/

    /*----------------------------导出数据------------------------------------*/
    /**
     * 仅导出表头的,使用于获取导入模板
     * @param response 返回数据
     * @param fields 表头集合
     */
    public void exportExcel(HttpServletResponse response,List<LinkedHashMap<String, Object>> fields)
    {
        exportExcel(response,fields,null);
    }

    /**
     * 将list数据源导入到excel表单
     * @param response 返回数据
     * @param fields 表头集合
     * @param list 导出数据集合
     */
    public void exportExcel(HttpServletResponse response,List<LinkedHashMap<String, Object>> fields,List<LinkedHashMap<String,Object>> list)
    {
        exportExcel(response, fields,list,null);
    }

    /**
     * 将list数据源导入到excel表单,指定sheetName
     * @param response 返回数据
     * @param fields 表头集合
     * @param list 导出数据集合
     * @param sheetName 工作表的名称
     */
    public void exportExcel(HttpServletResponse response, List<LinkedHashMap<String, Object>> fields,List<LinkedHashMap<String,Object>> list, String sheetName)
    {
        exportExcel(response,fields,list,sheetName,null);
    }

    /**
     * 将list数据源导入到excel表单,指定sheetName,指定导出文件名称
     * @param response 返回数据
     * @param fields 表头集合
     * @param list 导出数据集合
     * @param sheetName 工作表的名称
     * @param fileName 导出的文件名称
     */
    public void exportExcel(HttpServletResponse response, List<LinkedHashMap<String, Object>> fields,List<LinkedHashMap<String,Object>> list, String sheetName,String fileName)
    {
        exportExcel(response,fields,list,sheetName,fileName,null);
    }

    /**
     * 将list数据源导入到excel表单,指定sheetName,指定导出文件名称,设置标题
     * @param response 返回数据
     * @param fields 表头集合
     * @param list 导出数据集合
     * @param sheetName 工作表的名称
     * @param fileName 导出的文件名称
     * @param title title
     */
    public void exportExcel(HttpServletResponse response,List<LinkedHashMap<String, Object>> fields, List<LinkedHashMap<String,Object>> list, String sheetName,String fileName,String title)
    {
        this.type = Type.EXPORT;
        this.init(fields,list,sheetName,fileName,title);
        exportExcel(response,1);
    }

    /**
     * 将list对象数据源导入到excel表单,指定sheetName,指定导出文件名称,设置标题
     * @param response 请求体
     * @param clazz 实体类
     * @param objList 对象集合
     * @param sheetName 工作表的名称
     * @param fileName 导出的文件名称
     * @param coverFields 动态覆盖注解参数值
     * @param title title
     */
    public <T extends BaseEntity> void exportExcel(HttpServletResponse response,Class<T> clazz, List<T> objList,HashMap<String,HashMap<String,Object>> coverFields, String sheetName,String fileName,String title) throws Exception {
        this.type = Type.EXPORT;
        setFieldsByReadClassAttr(clazz,coverFields);
        List<LinkedHashMap<String,Object>> list = new ArrayList<>();
        if(objList != null){
            for (T obj : objList) {
                LinkedHashMap<String,Object> item = new LinkedHashMap<>();
                for (Object[] os : this.excelFields) {
                    Field field = (Field) os[0];
                    Excel excel = (Excel) os[1];
                    Object value = getTargetValue(obj, field, excel);
                    item.put(excel.name(),value);
                }
                list.add(item);
            }
        }
        this.init(null,list,sheetName,fileName,title);
        exportExcel(response,1);
    }

    /**
     * 多工作表导出
     * @param response 请求都信息
     * @param fieldsList 多工作表表头集合
     * @param muchList 多工作表数据集合
     * @param sheetNameList 多工作表的名称
     * @param fileName 导出的文件名称
     * @param titleList 多工作表title
     */
    public void exportExcelMuchWork(HttpServletResponse response,List<List<LinkedHashMap<String, Object>>> fieldsList, List<List<LinkedHashMap<String,Object>>> muchList, List<String> sheetNameList,String fileName,List<String> titleList){
        this.type = Type.EXPORT;
        this.fieldsList = fieldsList;
        this.muchList = muchList;
        this.sheetNameList = sheetNameList;
        this.titleList = titleList;
        this.init(fileName);
        exportExcel(response,2);
    }

    /**
     * 多工作表导出
     * @param response 请求头信息
     * @param clazzList 多工作表表头集合
     * @param muchList 多工作表数据集合
     * @param sheetNameList 多工作表的名称
     * @param fileName 导出的文件名称
     * @param titleList 多工作表title
     * @param coverFieldsList 动态覆盖注解参数值
     */
    public void exportExcelMuchObjList(
            HttpServletResponse response,
            List<Class<? extends BaseEntity>> clazzList,
            List<List<? extends BaseEntity>> muchList,
            List<String> sheetNameList,
            List<HashMap<String,HashMap<String,Object>>> coverFieldsList,
            String fileName,
            List<String> titleList
    ) throws Exception {
        this.type = Type.EXPORT;
        setFieldsByReadClassAttrMach(clazzList,coverFieldsList);
        if(muchList != null){
            for (int i = 0; i < muchList.size(); i++) {
                List<? extends BaseEntity> objList = muchList.get(i);
                List<Object[]> excelFields = this.excelFieldsList.get(i);
                List<LinkedHashMap<String,Object>> list = new ArrayList<>();
                for (int j = 0, objListSize = objList.size(); j < objListSize; j++) {
                    LinkedHashMap<String, Object> item = new LinkedHashMap<>();
                    for (Object[] os : excelFields) {
                        Field field = (Field) os[0];
                        Excel excel = (Excel) os[1];
                        Object value = getTargetValue(objList.get(j), field, excel);
                        item.put(excel.name(), value);
                    }
                    list.add(item);
                }
                this.muchList.add(list);
            }
        }
        this.sheetNameList = sheetNameList;
        this.titleList = titleList;
        this.init(fileName);
        exportExcel(response,2);
    }

    /**
     * 输出数据
     * @param response 请求体
     * @param oneOrMach 单个或者多个工作表
     */
    private void exportExcel(HttpServletResponse response,int oneOrMach)
    {
        try {
            if(oneOrMach == 1){
                writeSheet();//写入数据
            }else{
                writeSheetMuchWork();//写入数据
            }
            if(this.exportOrSave == 1){
                // 如果直接下载则输出,否则志写生成wb不输出
                setResponseHeader(response);
                wb.write(response.getOutputStream());//输出数据
            }else if(this.exportOrSave == 2){
                //把工作表保存到文件
                File file = new File(this.fileName);
                OutputStream ops = Files.newOutputStream(file.toPath());
                wb.write(ops);
                ops.close();
            }else if(this.exportOrSave == 3){
                //把工作表转MultipartFile
               this.multipartFile = workbookToCommonsMultipartFile(wb,this.fileName);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("导出Excel异常{}", e.getMessage());
        } finally {
            if(isAutoCloseWorkbook){
                IOUtils.closeQuietly(wb);
            }
        }
    }

    /**
     * 设置响应流信息
     * @param response 响应
     */
    private void setResponseHeader(HttpServletResponse response) {
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /*----------------------------其他辅助方法------------------------------------*/

    获取一些属性,作为工具方法/

    /**
     * 获取注解名称与字段的关系
     * @return HashMap<String,Field>
     */
    public HashMap<String,Field> getFieldJoinAttr(){
        return this.fieldJoinAttr;
    }

    /**
     * 获取注解名称与字段的关系
     * @return List<HashMap<String,Field>>
     */
    public List<HashMap<String,Field>> getFieldJoinAttrList(){
        return this.fieldJoinAttrList;
    }

    /**
     * 控制是直接导出还是返回文件自己处理
     * 1直接输出下载,2保存成文件到指定路径,3转换成MultipartFile
     * @param exportOrSave 设置参数
     */
    public void setExportOrSave(int exportOrSave){
        this.exportOrSave = exportOrSave;
    }

    /**
     * 获取表格
     * @return excel数据
     */
    public Workbook getWb() {
        return this.wb;
    }

    /**
     * 获取 MultipartFile
     * @return MultipartFile
     */
    public MultipartFile getMultipartFile() {
        return multipartFile;
    }

    /**
     * 关闭Workbook方法
     */
    public void closeWorkbook(){
        IOUtils.closeQuietly(wb);
    }

    /**
     * 获取表头属性已方便动态设置参数
     * @return 表头列表
     */
    public List<List<LinkedHashMap<String, Object>>> getFieldsList() {
        return fieldsList;
    }

    /**
     * 设置表头列表
     * @param fieldsList 表头列表
     */
    public void setFieldsList(List<List<LinkedHashMap<String, Object>>> fieldsList){
        this.fieldsList = fieldsList;
    }

    /**
     * 获取表头属性已方便动态设置参数
     * @return 表头列表
     */
    public List<LinkedHashMap<String, Object>> getFields() {
        return fields;
    }

    /**
     * 设置表头列表
     * @param fields 表头列表
     */
    public void setFields(List<LinkedHashMap<String, Object>> fields){
        this.fields = fields;
    }

    /**
     * 隐藏Excel中列属性
     * @param fields 列属性名
     */
    public void setExcludeFields(String[] fields)
    {
        this.excludeFields = fields;
    }

    /**
     * 隐藏Excel中列属性,根据类名
     * @param excludeFieldsByClassName 列属性名
     */
    public void setExcludeFieldsByClassName(HashMap<String,String[]> excludeFieldsByClassName){
        this.excludeFieldsByClassName = excludeFieldsByClassName;
    }

    /**
     * 多级类隐藏Excel中列属性
     * @param fields 注解的name
     */
    public void setExcludeExcelName(String[] fields){
        this.excludeExcelName = fields;
    }

    /**
     * 设置操作类型
     * @param type EXPORT:导出;IMPORT:导入
     */
    public void setType(Type type){
        this.type = type;
    }

    /**
     * 获取excel存在的表头
     */
    public List<HashMap<String, String>> getExcelHasHead() {
        return excelHasHead;
    }

    /**
     * 获取excel存在的表头
     */
    public HashMap<String, List<HashMap<String, String>>> getExcelHasHeadList() {
        return excelHasHeadList;
    }

    /


    /**
     * 保存excel的表头
     * @param name 表头名称
     */
    private void saveHeard(String name){
        if(this.fieldJoinAttr != null && this.fieldJoinAttr.containsKey(name)){
            Field field1 = this.fieldJoinAttr.get(name);
            HashMap<String,String> excelHasHead = new HashMap<>();
            excelHasHead.put("name",name);
            excelHasHead.put("key",field1.getName());
            this.excelHasHead.add(excelHasHead);
        }
    }

    /**
     * 通过实体类读取注解
     * @param clazz 实体类
     * @param coverFields 动态覆盖注解参数值
     */
    public <T extends BaseEntity> void setFieldsByReadClassAttr(Class<T> clazz,HashMap<String,HashMap<String,Object>> coverFields){
        this.excelFields = getFields(clazz);
        //根据指定顺序对属性排序
        this.excelFields = this.excelFields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
        List<Excel> excels = this.excelFields.stream().map(objects -> ((Excel) objects[1])).collect(Collectors.toList());
        this.fields = excelAnnotationToHashMap(excels,coverFields);
        Object[] objects = getFieldNameJoinAttrName(this.excelFields);
        this.fieldJoinAttr = (HashMap<String, Field>) objects[0];
        this.fieldJoinExcel = (HashMap<String, Excel>) objects[1];
    }

    /**
     * 多个实体类
     * 通过实体类读取注解
     * @param clazzList 实体类
     * @param coverFieldsList 动态覆盖注解参数值
     */
    public void setFieldsByReadClassAttrMach(List<Class<? extends BaseEntity>> clazzList,List<HashMap<String,HashMap<String,Object>>> coverFieldsList){
        this.excelFieldsList = new ArrayList<>();
        this.fieldsList = new ArrayList<>();
        this.fieldJoinAttrList = new ArrayList<>();
        this.fieldJoinExcelList = new ArrayList<>();
        int coverFieldsListSize = 0;
        if(coverFieldsList != null) {
             coverFieldsListSize = coverFieldsList.size();
        }
        for (int i = 0,j = clazzList.size(); i < j; i++) {
            List<Object[]> getFields = getFields(clazzList.get(i));
            //根据指定顺序对属性排序
            getFields = getFields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
            this.excelFieldsList.add(getFields);
            List<Excel> excels = getFields.stream().map(objects -> ((Excel) objects[1])).collect(Collectors.toList());
            HashMap<String,HashMap<String,Object>> coverFields = null;
            if(i < coverFieldsListSize && coverFieldsList.get(i) != null){
                coverFields = coverFieldsList.get(i);
            }
            List<LinkedHashMap<String,Object>> fields = excelAnnotationToHashMap(excels,coverFields);
            this.fieldsList.add(fields);
            Object[] objects = getFieldNameJoinAttrName(getFields);
            this.fieldJoinAttrList.add((HashMap<String, Field>) objects[0]);
            this.fieldJoinExcelList.add((HashMap<String, Excel>) objects[1]);
        }
    }

    /**
     * 把注解里面的值转换成hashMap
     * @param excels 注解列表
     * @param coverFields 动态覆盖注解参数值
     * @return List<LinkedHashMap<String,Object>>
     */
    private List<LinkedHashMap<String,Object>> excelAnnotationToHashMap(List<Excel> excels,HashMap<String,HashMap<String,Object>> coverFields){
        List<LinkedHashMap<String,Object>> fieldsList = new ArrayList<>();
        for (Excel excel : excels) {
            LinkedHashMap<String,Object> fields = new LinkedHashMap<>();
            fields.put("sort",excel.sort());
            fields.put("name",excel.name());
            fields.put("dateFormat",excel.dateFormat());
            fields.put("readConverterExp",excel.readConverterExp());
            fields.put("separator",excel.separator());
            fields.put("scale",excel.scale());
            fields.put("roundingMode",excel.roundingMode());
            fields.put("height",excel.height());
            fields.put("width",excel.width());
            fields.put("suffix",excel.suffix());
            fields.put("defaultValue",excel.defaultValue());
            fields.put("prompt",excel.prompt());
            fields.put("combo",excel.combo());
            fields.put("needMerge",excel.needMerge());
            fields.put("isExport",excel.isExport());
            fields.put("targetAttr",excel.targetAttr());
            fields.put("isStatistics",excel.isStatistics());
            fields.put("cellType",excel.cellType());
            fields.put("headerBackgroundColor",excel.headerBackgroundColor());
            fields.put("headerColor",excel.headerColor());
            fields.put("headerCellComment",excel.headerCellComment());
            fields.put("backgroundColor",excel.backgroundColor());
            fields.put("color",excel.color());
            fields.put("align",excel.align());
            fields.put("handler",excel.handler());
            fields.put("args",excel.args());
            fields.put("type",excel.type());

            if(coverFields != null && coverFields.containsKey(excel.name())){
                HashMap<String,Object> coverField = coverFields.get(excel.name());
                Set<String> keySet = coverField.keySet();
                for (String s : keySet) {
                    fields.put(s,coverField.get(s));
                }
            }
            fieldsList.add(fields);
        }
        return fieldsList;
    }

    /**
     * 获取注解的字段中文名称与字段的属性名称对应关系
     * @param excelFields 注解列表
     * @return HashMap<String,Field>
     */
    private Object[] getFieldNameJoinAttrName(List<Object[]> excelFields){
        Object[] objects = new Object[2];
        HashMap<String,Field> fieldJoinAttr = new HashMap<>();
        HashMap<String,Excel> fieldJoinExcel = new HashMap<>();
        for (Object[] excelField : excelFields) {
            Field field = (Field) excelField[0];
            Excel attr = (Excel) excelField[1];
            fieldJoinAttr.put(attr.name(),field);
            fieldJoinExcel.put(attr.name(),attr);
        }
        objects[0] = fieldJoinAttr;
        objects[1] = fieldJoinExcel;
        return objects;
    }

    /**
     * 通过实体类获取添加了注解的属性字段和注解参数信息
     */
    private <T> List<Object[]> getFields(Class<T> clazz)
    {
        List<Object[]> fields = new ArrayList<>();
        List<Field> tempFields = new ArrayList<>();
        //获取实体类父级的所有属性
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        //获取实体类所有属性
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        for (Field field : tempFields) {
            //过滤掉需要排除的属性
            String classSimpleName = clazz.getSimpleName();
            if (!ArrayUtils.contains(this.excludeFields, field.getName()) && (!this.excludeFieldsByClassName.containsKey(classSimpleName) || !ArrayUtils.contains(this.excludeFieldsByClassName.get(classSimpleName), field.getName()))) {
                // 获取单注解,有Excel注解的属性
                if (field.isAnnotationPresent(Excel.class)) {
                    //获取字段元素的指定类型的注释
                    Excel attr = field.getAnnotation(Excel.class);
                    //判断是否当前操作类型
                    if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {
                        //通过setAccessible(true)的方式关闭Java 语言访问安全检查就可以达到提升反射速度的目的
                        field.setAccessible(true);
                        //把字段和注解内容添加到fields
                        fields.add(new Object[]{field, attr});
                    }
                }

                // 获取多注解,有Excels注解的属性
                if (field.isAnnotationPresent(Excels.class)) {
                    Excels attrs = field.getAnnotation(Excels.class);
                    Excel[] excels = attrs.value();
                    for (Excel attr : excels) {
                        if (attr != null && !ArrayUtils.contains(this.excludeExcelName, attr.name())) {
                            if (attr.type() == Type.ALL || attr.type() == type) {
                                field.setAccessible(true);
                                fields.add(new Object[]{field, attr});
                            }
                        }
                    }
                }
            }
        }
        return fields;
    }

    /**
     * 初始化数据方法
     * @param fields    表头数据
     * @param list      数据
     * @param sheetName sheetName
     * @param fileName  导出文件名称
     * @param title     表格标题
     */
    private void init(List<LinkedHashMap<String, Object>> fields,List<LinkedHashMap<String,Object>> list,String sheetName,String fileName,String title)
    {
        if(fields != null){
            this.fields = fields;
        }
        if(list != null){
            this.list = list;
        }
        this.sheetName = sheetName == null ? "Sheet":sheetName;
        this.fileName = fileName == null ? createFileName():fileName;
        if (!this.fileName.contains(".xls")) {
            this.fileName += ".xlsx";
        }
        this.title = title;
        this.maxHeight = getRowHeight();
        createWorkbook();
        createTitle();
    }

    /**
     * 初始化数据方法
     *
     * @param fileName 标题
     */
    private void init(String fileName)
    {
        this.fields = fieldsList.get(0);
        if(muchList != null && muchList.size() > 0){
            this.list = muchList.get(0);
            if(list == null){
                this.list = new ArrayList<>();
            }
        }
        String sheetName = null;
        if(sheetNameList != null){
            sheetName = sheetNameList.get(0);
        }
        this.sheetName = sheetName == null ? "Sheet":sheetName;
        this.fileName = fileName == null ? createFileName():fileName;
        if (!this.fileName.contains(".xls")) {
            this.fileName += ".xlsx";
        }
        this.title = titleList != null ? titleList.get(0):null;
        this.maxHeight = getRowHeight();
        createWorkbook();
        createTitle();
    }

    /**
     * 以日期创建文件名称
     * @return 文件名称
     */
    private String createFileName(){
        SimpleDateFormat sdf = new SimpleDateFormat();// 格式化时间
        sdf.applyPattern("yyyyMMddHHmmss");
        return "Excel-" + sdf.format( new Date()) + ".xlsx";
    }

    /**
     * 创建一个工作簿
     */
    private void createWorkbook()
    {
        this.wb = new SXSSFWorkbook(500);
        this.sheet = wb.createSheet();
        wb.setSheetName(0, sheetName);
        this.styles = createStyles(wb);
    }

    /**
     * 创建表格样式
     * @param wb 工作薄对象
     * @return 样式列表
     */
    private Map<String, CellStyle> createStyles(Workbook wb)
    {
        //创建一个map存放样式
        Map<String, CellStyle> styles = new HashMap<>();
        //创建标题单元格的样式
        CellStyle style = wb.createCellStyle(); //创建一个新样式
        style.setAlignment(HorizontalAlignment.CENTER);//设置水平居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);//设置垂直居中
        Font titleFont = wb.createFont();//创建一个字体
        titleFont.setFontName("Arial");//设置字体
        titleFont.setFontHeightInPoints((short) 16);//设置字体高度
        titleFont.setBold(true);//设置粗体
        style.setFont(titleFont);//将字体应用到这个样式
        styles.put("title", style);
        //创建数据单元格的样式
        style = wb.createCellStyle();//创建一个新样式
        style.setAlignment(HorizontalAlignment.CENTER);//设置水平居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);//设置垂直居中
        style.setBorderRight(BorderStyle.THIN);//设置右边框
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());//设置右边框颜色
        style.setBorderLeft(BorderStyle.THIN);//设置左边框
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());//设置左边框颜色
        style.setBorderTop(BorderStyle.THIN);//设置上边框
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());//设置上边框颜色
        style.setBorderBottom(BorderStyle.THIN);//设置下边框
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());//设置下边框颜色
        Font dataFont = wb.createFont();//创建一个字体
        dataFont.setFontName("Arial");//设置字体
        dataFont.setFontHeightInPoints((short) 10);//设置字体高度
        style.setFont(dataFont);//将字体应用到这个样式
        styles.put("data", style);
        //创建表头样式
        style = wb.createCellStyle();//创建一个新样式
        style.cloneStyleFrom(styles.get("data"));//克隆一个样式
        style.setAlignment(HorizontalAlignment.CENTER);//设置水平居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);//设置垂直居中
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());//设置背景色
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);//设置填充模式
        Font headerFont = wb.createFont();//创建一个字体
        headerFont.setFontName("Arial");//设置字体
        headerFont.setFontHeightInPoints((short) 10);//设置字体高度
        headerFont.setBold(true);//设置粗体
        headerFont.setColor(IndexedColors.WHITE.getIndex());//设置字体颜色
        style.setFont(headerFont);//将字体应用到这个样式
        styles.put("header", style);
        //创建合计行样式
        style = wb.createCellStyle();//创建一个新样式
        style.setAlignment(HorizontalAlignment.CENTER);//设置水平居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);//设置垂直居中
        Font totalFont = wb.createFont();//创建一个字体
        totalFont.setFontName("Arial");//设置字体
        totalFont.setFontHeightInPoints((short) 10);//设置字体高度
        style.setFont(totalFont);//将字体应用到这个样式
        styles.put("total", style);
        return styles;
    }

    /**
     * 根据表头设置取最大行高
     */
    private short getRowHeight()
    {
        double maxHeight = 14;
        for (LinkedHashMap<String,Object> os : this.fields) {
            if(os.containsKey("height")){
                maxHeight = Math.max(maxHeight, (double)os.get("height"));
            }
        }
        return (short) (maxHeight * 20);
    }

    /**
     * 创建excel标题(表格大标题)一般不需要
     */
    private void createTitle() {
        //判断需不需要设置标题
        if (StringUtils.isNotEmpty(title)) {
            Row titleRow = sheet.createRow(rowNum == 0 ? rowNum++ : 0);
            titleRow.setHeightInPoints(30);
            Cell titleCell = titleRow.createCell(0);
            titleCell.setCellStyle(styles.get("title"));
            titleCell.setCellValue(title);
            //合并单元格,CellRangeAddress CellRangeAddress有4个参数:起始行号,终止行号, 起始列号,终止列号
            //是对当前sheet做合并,可以在任何地方设置,只要参数设置正确,注意合并的单元格只需要一次赋值
            sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), this.fields.size() - 1));
        }
    }

    /**
     * 创建工作表
     * @param index 序号
     */
    private void createSheet(int index)
    {
        //如果需要的工作表数量大于1个并当前工作表不是第一个,就创建下一个新的工作表
        if (sheetIndex > 0) {
            this.sheet = wb.createSheet();
            this.createTitle();
            wb.setSheetName(sheetIndex, sheetName + (index > 0 ? "-"+ index:""));
        }
    }

    /**
     * 把数据写入到excel
     */
    private void writeSheetMuchWork()
    {
        for (int i = 0,size = fieldsList.size(); i < size;i++){
            this.fields = fieldsList.get(i);
            //要写到当前工作表的数据
            if(muchList != null && !muchList.isEmpty() && i < muchList.size()){
                this.list = muchList.get(i);
            }else{
                this.list = new ArrayList<>();
            }
            //当前工作表名称
            if(sheetNameList != null && !sheetNameList.isEmpty() && i < sheetNameList.size()){
                this.sheetName = sheetNameList.get(i);
            }else{
                this.sheetName = "Sheet" + i;
            }
            //当前工作表title
            if(titleList != null && !titleList.isEmpty() && i < titleList.size()){
                this.title = titleList.get(i);
            }else{
                this.title = null;
            }
            //当前工作表索引
            if(i > 0){
                sheetIndex++;
            }
            //当前工作表最大高度
            this.maxHeight = getRowHeight();
            writeSheet();
        }
    }

    /**
     * 写入数据到Sheet
     */
    private void writeSheet()
    {
        // 根据工作表支持的最大行数和数据总量计算一共需要多少个seethe
        int sheetNumber = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
        for (int index = 0; index < sheetNumber; index++) {
            if(sheetIndex > 0 && index > 0){
                sheetIndex++;
            }
            //根据索引创建工作表
            createSheet(index);
            // 产生一行
            Row row = sheet.createRow(rowNum);
            int column = 0;
            // 写入表头名称
            for (LinkedHashMap<String,Object> field : fields) {
                this.createCell(field, row, column++);
            }
            if (Type.EXPORT.equals(type) && list != null) {
                fillExcelData(index);//写入单元格数据
                addStatisticsRow();//添加合计行
            }
        }
    }

    /**
     * 创建表头单元格
     */
    private void createCell(LinkedHashMap<String,Object> field, Row row, int column)
    {
        // 创建列
        Cell cell = row.createCell(column);
        // 写入表头信息
        cell.setCellValue((String) field.get("name"));
        setDataValidation(field, column);

        CellStyle style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("header"));//取出默认样式
        //根据field的样式需求覆盖默认样式
        if(field.containsKey("headerBackgroundColor")){
            IndexedColors headerBackgroundColor = (IndexedColors)field.get("headerBackgroundColor");
            style.setFillForegroundColor(headerBackgroundColor.getIndex());
        }
        if(field.containsKey("headerColor")){
            Font headerFont = wb.createFont();//创建一个字体
            headerFont.setFontName("Arial");//设置字体
            headerFont.setFontHeightInPoints((short) 10);//设置字体高度
            headerFont.setBold(true);//设置粗体
            IndexedColors headerColor = (IndexedColors)field.get("headerColor");
            headerFont.setColor(headerColor.getIndex());//设置字体颜色
            style.setFont(headerFont);//将字体应用到这个样式
        }
        cell.setCellStyle(style);//设置单元格样式
        if(field.containsKey("headerCellComment")){
            String headerCellComment = (String) field.get("headerCellComment");
            if(headerCellComment != null && headerCellComment.length() > 0){
                Drawing<?> drawing = sheet.createDrawingPatriarch();
                //dx1- 第一个单元格内的 x 坐标。
                //dy1- 第一个单元格内的 y 坐标。
                //dx2- 第二个单元格内的 x 坐标。
                //dy2- 第二个单元格内的 y 坐标。
                //col1- 第一个单元格的列(从 0 开始)。
                //row1- 第一个单元格的行(从 0 开始)。
                //col2- 第二个单元格的列(从 0 开始)。
                //row2- 第二个单元格的行(从 0 开始)。
                Comment comment = drawing.createCellComment(new XSSFClientAnchor(0,0,0,0,column,row.getRowNum(),column,row.getRowNum()));
                //输入批注信息
                comment.setString(new XSSFRichTextString(headerCellComment));
                //添加批注作者
                comment.setAuthor("程序员");
                cell.setCellComment(comment);//设置单元格批注
            }
        }

    }

    /**
     * 设置单元格宽度
     */
    private void setDataValidation(LinkedHashMap<String,Object> field, int column)
    {
        String fieldName = (String)field.get("name");
        if (fieldName.contains("注:")) {//如果列名中包含注字就把宽度设宽一点
            sheet.setColumnWidth(column, 6000);
        } else {
            double width = 16;
            if(field.containsKey("width")){
                width = (double)field.get("width");
            }
            // 设置列宽
            sheet.setColumnWidth(column, (int) ((width + 0.72) * 256));
        }
        String[] combo = new String[]{};
        String prompt = "";
        if(field.containsKey("combo")) {
            combo = (String[]) field.get("combo");
        }
        if(field.containsKey("prompt")) {
            prompt = (String) field.get("prompt");
        }
        if (StringUtils.isNotEmpty(prompt) || combo.length > 0){
            // 如果设置了提示信息或只能选择不能输入的列内容.
            setPromptOrValidation(sheet,combo , prompt, column, column);
        }
    }

    /**
     * 设置 POI XSSFSheet 单元格提示或选择框
     *
     * @param sheet         表单
     * @param textList      下拉框显示的内容
     * @param promptContent 提示内容
     * @param firstCol      开始列
     * @param endCol        结束列
     */
    private void setPromptOrValidation(Sheet sheet, String[] textList, String promptContent, int firstCol, int endCol)
    {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        DataValidationConstraint constraint = textList.length > 0 ? helper.createExplicitListConstraint(textList) : helper.createCustomConstraint("DD1");
        CellRangeAddressList regions = new CellRangeAddressList(1, 100, firstCol, endCol);
        DataValidation dataValidation = helper.createValidation(constraint, regions);
        if (StringUtils.isNotEmpty(promptContent)) {
            // 如果设置了提示信息则鼠标放上去提示
            dataValidation.createPromptBox("", promptContent);
            dataValidation.setShowPromptBox(true);
        }
        // 处理Excel兼容性问题
        if (dataValidation instanceof XSSFDataValidation) {
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
        } else {
            dataValidation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(dataValidation);
    }

    /**
     * 写入excel数据
     *
     * @param index sheet序号
     */
    private void fillExcelData(int index)
    {
        int startNo = index * sheetSize;//计算当前工作表的第一个数据行
        int endNo = Math.min(startNo + sheetSize, list.size());//计算当前工作表的最后一个数据行
        for (int i = startNo; i < endNo; i++) {
            int rowNo = i + 1 + rowNum - startNo;
            Row row = sheet.createRow(rowNo);//计算行号索引创建行
            row.setHeight(maxHeight); // 设置行高
            LinkedHashMap<String,Object> data = list.get(i);
            int column = 0;
            for (LinkedHashMap<String,Object> field : fields) {
                this.addCell(field, row, data, column++);
            }
        }
    }

    /**
     * 添加单元格
     */
    private void addCell(LinkedHashMap<String,Object> field, Row row, LinkedHashMap<String,Object> data, int column)
    {
        Cell cell;
        try {
            // 根据设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
            boolean isExport = true;
            if(field.containsKey("isExport")){
                isExport = (boolean)field.get("isExport");
            }
            String fieldName = (String) field.get("name");
            Object value;
            cell = row.createCell(column);// 创建列
            setDataCell(cell, field);//设置样式
            if (isExport && data.containsKey(fieldName)) {
                //对单元格动态设置合并,通过把值设置成HashMap,value是值,mergeRows是合并行数
                Object dataObj = data.get(fieldName);
                int mergeRows = 0;
                if(dataObj instanceof HashMap<?,?>){
                    HashMap<?,?> dataHashMap = (HashMap<?,?>) dataObj;
                    value = dataHashMap.get("value");
                    if(dataHashMap.containsKey("mergeRows")){
                        Object dd = dataHashMap.get("mergeRows");
                        if(dd instanceof Integer){
                            mergeRows = (int)dd;
                        }
                    }
                }else{
                    value = dataObj;
                }

                String dateFormat = "";
                if(field.containsKey("dateFormat")){
                    dateFormat = (String) field.get("dateFormat");
                }
                String readConverterExp = "";
                if(field.containsKey("readConverterExp")){
                    readConverterExp = (String) field.get("readConverterExp");
                }
                String separator = ",";
                if(field.containsKey("separator")){
                    separator = (String) field.get("separator");
                }
                int scale = -1;
                if(field.containsKey("scale")){
                    scale = (int) field.get("scale");
                }
                Class<?> handler = ExcelHandlerAdapter.class;
                if(field.containsKey("handler")){
                    handler = (Class<?>) field.get("handler");
                }
                if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) {
                    //如果是时间
                    cell.setCellValue(parseDateToStr(dateFormat, value));//设置单元格值
                } else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) {
                    //如果设置内容转表达式
                    cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));//设置单元格值
                } else if (value instanceof BigDecimal && -1 != scale) {
                    //如果是高精度数值类型并且开启了高精度格式化
                    //setScale 第一个参数是小数点后保留的位数;第二个参数是格式化的模式,默认四舍五入
                    int roundingMode = BigDecimal.ROUND_HALF_EVEN;
                    if(field.containsKey("roundingMode")){
                        roundingMode = (int)field.get("roundingMode");
                    }
                    cell.setCellValue((((BigDecimal) value).setScale(scale, roundingMode)).toString());//设置单元格值
                } else if (!handler.equals(ExcelHandlerAdapter.class)) {
                    //如果设置了自定义数据处理器
                    String[] args = new  String[]{};
                    if(field.containsKey("args")){
                        args = (String[]) field.get("args");
                    }
                    cell.setCellValue(dataFormatHandlerAdapter(value, handler,args));//设置单元格值
                } else {
                    // 设置列类型
                    setCellVo(value, field, cell);
                }
                addStatisticsData(column, Convert.toStr(value), field);//添加合计信息
                if(mergeRows > 1){
                    //合并单元格,CellRangeAddress CellRangeAddress有4个参数:起始行号,终止行号, 起始列号,终止列号
                    //是对当前sheet做合并,可以在任何地方设置,只要参数设置正确,注意合并的单元格只需要一次赋值
                    CellRangeAddress cellRange = new CellRangeAddress(row.getRowNum(), row.getRowNum() + mergeRows -1, column, column);
                    sheet.addMergedRegion(cellRange);
                    //为合并单元格添加边框
                    RegionUtil.setBorderTop(BorderStyle.THIN, cellRange, sheet);
                    RegionUtil.setBorderBottom(BorderStyle.THIN, cellRange, sheet);
                    RegionUtil.setBorderLeft(BorderStyle.THIN, cellRange, sheet);
                    RegionUtil.setBorderRight(BorderStyle.THIN, cellRange, sheet);
                }
            }
        } catch (Exception e) {
            log.error("导出Excel失败{}", e.getMessage());
        }
    }

    /**
     * 获取bean中的属性值
     * @param vo 实体对象
     * @param field 字段
     * @param excel 注解
     * @return 最终的属性值
     * @throws Exception Exception
     */
    private <T>  Object getTargetValue(T vo, Field field, Excel excel) throws Exception {
        //取得对象的Field属性值
        Object o = field.get(vo);
        //如果targetAttr不为空,表示是另一个类
        if (StringUtils.isNotEmpty(excel.targetAttr())) {
            String target = excel.targetAttr();
            if (target.contains(".")) {
                //如果是多级类嵌套,循环获取到最终的值
                String[] targets = target.split("[.]");
                if(o instanceof List) {
                    //如果是列表,则循环把值取出来拼接在一起
                    List<?> objects = (List<?>)o;
                    StringJoiner valStr =  new StringJoiner(",");
                    for (Object object : objects) {
                        Object val = "";
                        for (String name : targets) {
                            val = getValue(object, name);
                        }
                        valStr.add(String.valueOf(val));
                    }
                    o = valStr.toString();
                }else{
                    for (String name : targets) {
                        if(o instanceof List) {
                            List<?> objects = (List<?>)o;
                            StringJoiner valStr =  new StringJoiner(",");
                            for (Object object : objects) {
                                valStr.add(String.valueOf(getValue(object, name)));
                            }
                            o = valStr.toString();
                        }else{
                            o = getValue(o, name);
                        }
                    }
                }
            } else {
                //不是多级嵌套,直接获取子类的属性值
                if(o instanceof List){
                    //如果是列表,则循环把值取出来拼接在一起
                    List<?> objects = (List<?>)o;
                    StringJoiner valStr =  new StringJoiner(",");
                    for (Object object : objects) {
                        valStr.add(String.valueOf(getValue(object, target)));
                    }
                    o = valStr.toString();
                }else{
                    o = getValue(o, target);
                }
            }
        }
        if(o instanceof List) {
            //如果是列表,则循环把值取出来拼接在一起
            List<?> objects = (List<?>) o;
            StringJoiner valStr = new StringJoiner(",");
            for (Object object : objects) {
                valStr.add(String.valueOf(object));
            }
            o = valStr.toString();
        }
        return o;
    }

    /**
     * 以类的属性的get方法方法形式获取值
     * @param o 实体类
     * @param name 属性名称
     * @return value 值
     * @throws Exception Exception
     */
    private Object getValue(Object o, String name) throws Exception {
        if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) {
            //获取操作类的Class对象
            Class<?> clazz = o.getClass();
            //获取属性对象
            Field field = clazz.getDeclaredField(name);
            //设置不要安全检查,已提高速度
            field.setAccessible(true);
            //取得对象的Field属性值
            o = field.get(o);
        }
        return o;
    }

    /**
     * 设置单元格样式
     *
     * @param cell  单元格
     * @param field 字段信息
     */
    private void setDataCell(Cell cell, LinkedHashMap<String,Object> field)
    {
        CellStyle style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        HorizontalAlignment align = HorizontalAlignment.CENTER;
        if(field.containsKey("align")){
            align = (HorizontalAlignment) field.get("align");
        }
        style.setAlignment(align);
        Font dataFont = wb.createFont();
        dataFont.setFontName("Arial");
        dataFont.setFontHeightInPoints((short) 10);
        IndexedColors color = IndexedColors.BLACK;
        if(field.containsKey("color")){
            color = (IndexedColors) field.get("color");
        }
        dataFont.setColor(color.getIndex());
        style.setFont(dataFont);
        if(field.containsKey("backgroundColor")){
            IndexedColors headerBackgroundColor = (IndexedColors)field.get("backgroundColor");
            style.setFillForegroundColor(headerBackgroundColor.getIndex());
        }
        cell.setCellStyle(style);
    }

    /**
     * 格式化不同类型的日期对象
     *
     * @param dateFormat 日期格式
     * @param val 被格式化的日期对象
     * @return 格式化后的日期字符
     */
    private String parseDateToStr(String dateFormat, Object val)
    {
        if (val == null) {
            return "";
        }
        String str;
        if (val instanceof Date) {
            str = DateUtils.parseDateToStr(dateFormat, (Date) val);
        } else if (val instanceof LocalDateTime) {
            str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val));
        } else if (val instanceof LocalDate) {
            str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val));
        } else if (val instanceof Long) {
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            str = sdf.format(val);
        } else {
            str = val.toString();
        }
        return str;
    }

    /**
     * 解析导出值
     * @param propertyValue 参数值
     * @param converterExp 表达式,如:"0=男,1=女,2=未知"
     * @param separator 分隔符
     * @return 解析后值
     */
    private static String convertByExp(String propertyValue, String converterExp, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(separator, propertyValue)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[0].equals(value)) {
                        propertyString.append(itemArray[1]).append(separator);
                        break;
                    }
                }
            } else {
                if (itemArray[0].equals(propertyValue)) {
                    return itemArray[1];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }

    /**
     * 反向解析值
     * @param propertyValue 参数值
     * @param converterExp 转义表达式,如:”男=0,女=1,未知=2“
     * @param separator 分隔符
     * @return 解析后值
     */
    private static String reverseByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(separator, propertyValue)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[1].equals(value)) {
                        propertyString.append(itemArray[0]).append(separator);
                        break;
                    }
                }
            } else {
                if (itemArray[1].equals(propertyValue)) {
                    return itemArray[0];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }

    /**
     * 数据处理器
     * @param value 数据值
     * @param clazz 自定义的数据处理器
     * @param args 自定义的数据处理器的参数
     * @return 处理后的值
     */
    private String dataFormatHandlerAdapter(Object value, Class<?> clazz,String[] args)
    {
        try {
            Object instance =clazz.newInstance();
            Method formatMethod = clazz.getMethod("format", Object.class, String[].class);
            value = formatMethod.invoke(instance, value,args);
        } catch (Exception e) {
            log.error("不能格式化数据" + clazz + "错误信息:{}", e.getMessage());
        }
        return Convert.toStr(value);
    }

    /**
     * 设置单元格信息
     * @param value 单元格值
     * @param field 字段信息
     * @param cell 单元格
     */
    private void setCellVo(Object value, LinkedHashMap<String,Object> field, Cell cell)
    {
        //导出类型(0数字 1字符串) NUMERIC(0), STRING(1), IMAGE(2);
        ColumnType cellType = ColumnType.STRING;
        if(field.containsKey("cellType")){
            cellType = (ColumnType) field.get("cellType");
        }
        if (ColumnType.STRING == cellType) {
            //数据类型为字符串类型
            String cellValue = Convert.toStr(value);
            // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。
            if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) {
                cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0");
            }
            String defaultValue = "";
            if(field.containsKey("defaultValue")){
                defaultValue = (String) field.get("defaultValue");
            }
            //后缀
            String suffix = "";
            if(field.containsKey("suffix")){
                suffix = (String) field.get("suffix");
            }
            //当值为空时设置默认值
            cell.setCellValue(StringUtils.isNull(cellValue) ? defaultValue : cellValue + suffix);//设置单元格值
        } else if (ColumnType.NUMERIC == cellType) {
            //数据类型为数字类型
            if (StringUtils.isNotNull(value)) {
                cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));//设置单元格值
            }
        } else if (ColumnType.IMAGE == cellType) {
            //如果是图片类型
            ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);
            String imagePath = Convert.toStr(value);
            if (StringUtils.isNotEmpty(imagePath)) {
                byte[] data = ImageUtils.getImage(imagePath);
                getDrawingPatriarch(cell.getSheet()).createPicture(anchor, cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
            }
        }
    }

    /**
     * 获取画布
     */
    private static Drawing<?> getDrawingPatriarch(Sheet sheet)
    {
        if (sheet.getDrawingPatriarch() == null) {
            sheet.createDrawingPatriarch();
        }
        return sheet.getDrawingPatriarch();
    }

    /**
     * 获取图片类型,设置图片插入类型
     */
    private int getImageType(byte[] value)
    {
        String type = FileTypeUtils.getFileExtendName(value);
        if ("JPG".equalsIgnoreCase(type)) {
            return Workbook.PICTURE_TYPE_JPEG;
        } else if ("PNG".equalsIgnoreCase(type)) {
            return Workbook.PICTURE_TYPE_PNG;
        }
        return Workbook.PICTURE_TYPE_JPEG;
    }

    /**
     * 合计统计信息
     */
    private void addStatisticsData(Integer index, String text,LinkedHashMap<String,Object> field)
    {
        boolean isStatistics = false;
        if(field.containsKey("isStatistics")){
            isStatistics = (boolean)field.get("isStatistics");
        }
        if (isStatistics) {
            //如果需要做统计
            Double temp = 0D;
            if (!statistics.containsKey(index)) {
                statistics.put(index, temp);
            }
            try {
                temp = Double.valueOf(text);
            } catch (NumberFormatException e) {
                System.out.println(e.getMessage());
            }
            statistics.put(index, statistics.get(index) + temp);
        }
    }

    /**
     * 创建统计行
     */
    private void addStatisticsRow()
    {
        if (statistics.size() > 0) {
            //如果有合计信息
            Row row = sheet.createRow(sheet.getLastRowNum() + 1);//在最后创建新行写入合计数据
            Set<Integer> keys = statistics.keySet();
            Cell cell = row.createCell(0);
            cell.setCellStyle(styles.get("total"));
            cell.setCellValue("合计");
            for (Integer key : keys) {
                cell = row.createCell(key);
                cell.setCellStyle(styles.get("total"));
                cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
            }
            statistics.clear();
        }
    }

    /**
     * 判断是否是空行
     * @param row 判断的行
     * @return 结果
     */
    private boolean isRowEmpty(Row row)
    {
        if (row == null) {
            return true;
        }
        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
            Cell cell = row.getCell(i);
            if (cell != null && cell.getCellType() != CellType.BLANK) {
                return false;
            }
        }
        return true;
    }

    /**
     * Workbook 转 MultipartFile(CommonsMultipartFile)
     * @param workbook excel文档
     * @param fileName 文件名
     * @return MultipartFile
     */
    public static MultipartFile workbookToCommonsMultipartFile(Workbook workbook,String fileName) {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            workbook.write(bos);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        byte[] barray = bos.toByteArray();
        //is第一转
        InputStream is = new ByteArrayInputStream(barray);
        try {
            return new MockMultipartFile(fileName,fileName,"application/vnd.ms-excel",is);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值