第一次发帖,不足之处大佬们请指点一下,谢谢,
所需jar包
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>${poi.version}</version></dependency><!-- excel POI 工具 --><dependency><groupId>org.apache.poi</groupId><artifactId>ooxml-schemas</artifactId><version>1.3</version></dependency>
自定义导出Excel数据注解
/*** 自定义导出Excel数据注解** @author*/@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Excel {/*** 导出到Excel中的名字.*/public String name() default "";/*** 日期格式, 如: yyyy-MM-dd*/public String dateFormat() default "";/*** 读取内容转表达式 (如: 0=男,1=女,2=未知)*/public String readConverterExp() default "";/*** 导出类型(0数字 1字符串)*/public ColumnType cellType() default ColumnType.STRING;/*** 导出时在excel中每个列的高度 单位为字符*/public double height() default 14;/*** 导出时在excel中每个列的宽 单位为字符*/public double width() default 16;/*** 文字后缀,如% 90 变成90%*/public String suffix() default "";/*** 当值为空时,字段的默认值*/public String defaultValue() default "";/*** 提示信息*/public String prompt() default "";/*** 设置只能选择不能输入的列内容.*/public String[] combo() default {};/*** 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.*/public boolean isExport() default true;/*** 另一个类中的属性名称,支持多级获取,以小数点隔开*/public String targetAttr() default "";/*** 字段类型(0:导出导入;1:仅导出;2:仅导入)*/Type type() default Type.ALL;public enum Type {ALL(0), EXPORT(1), IMPORT(2);private final int value;Type(int value) {this.value = value;}public int value() {return this.value;}}public enum ColumnType {NUMERIC(0), STRING(1);private final int value;ColumnType(int value) {this.value = value;}public int value() {return this.value;}}}/*** 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.** @author ruoyi*/@SuppressWarnings("rawtypes")public class ReflectUtils {private static final String SETTER_PREFIX = "set";private static final String GETTER_PREFIX = "get";private static final String CGLIB_CLASS_SEPARATOR = "$$";private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);/*** 调用Getter方法.* 支持多级,如:对象名.对象名.方法*/@SuppressWarnings("unchecked")public static <E> E invokeGetter(Object obj, String propertyName) {Object object = obj;for (String name : StringUtils.split(propertyName, ".")) {String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});}return (E) object;}/*** 调用Setter方法, 仅匹配方法名。* 支持多级,如:对象名.对象名.方法*/public static <E> void invokeSetter(Object obj, String propertyName, E value) {Object object = obj;String[] names = StringUtils.split(propertyName, ".");for (int i = 0; i < names.length; i++) {if (i < names.length - 1) {String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});} else {String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);invokeMethodByName(object, setterMethodName, new Object[]{value});}}}/*** 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.*/@SuppressWarnings("unchecked")public static <E> E getFieldValue(final Object obj, final String fieldName) {Field field = getAccessibleField(obj, fieldName);if (field == null) {logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");return null;}E result = null;try {result = (E) field.get(obj);} catch (IllegalAccessException e) {logger.error("不可能抛出的异常{}", e.getMessage());}return result;}/*** 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.*/public static <E> void setFieldValue(final Object obj, final String fieldName, final E value) {Field field = getAccessibleField(obj, fieldName);if (field == null) {// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");return;}try {field.set(obj, value);} catch (IllegalAccessException e) {logger.error("不可能抛出的异常: {}", e.getMessage());}}/*** 直接调用对象方法, 无视private/protected修饰符.* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.* 同时匹配方法名+参数类型,*/@SuppressWarnings("unchecked")public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,final Object[] args) {if (obj == null || methodName == null) {return null;}Method method = getAccessibleMethod(obj, methodName, parameterTypes);if (method == null) {logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");return null;}try {return (E) method.invoke(obj, args);} catch (Exception e) {String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";throw convertReflectionExceptionToUnchecked(msg, e);}}/*** 直接调用对象方法, 无视private/protected修饰符,* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.* 只匹配函数名,如果有多个同名函数调用第一个。*/@SuppressWarnings("unchecked")public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args) {Method method = getAccessibleMethodByName(obj, methodName, args.length);if (method == null) {// 如果为空不报错,直接返回空。logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");return null;}try {// 类型转换(将参数数据类型转换为目标方法参数类型)Class<?>[] cs = method.getParameterTypes();for (int i = 0; i < cs.length; i++) {if (args[i] != null && !args[i].getClass().equals(cs[i])) {if (cs[i] == String.class) {args[i] = Convert.toStr(args[i]);if (StringUtils.endsWith((String) args[i], ".0")) {args[i] = StringUtils.substringBefore((String) args[i], ".0");}} else if (cs[i] == Integer.class) {args[i] = Convert.toInt(args[i]);} else if (cs[i] == Long.class) {args[i] = Convert.toLong(args[i]);} else if (cs[i] == Double.class) {args[i] = Convert.toDouble(args[i]);} else if (cs[i] == Float.class) {args[i] = Convert.toFloat(args[i]);} else if (cs[i] == Date.class) {if (args[i] instanceof String) {args[i] = DateUtils.parseDate(args[i]);} else {args[i] = DateUtil.getJavaDate((Double) args[i]);}}}}return (E) method.invoke(obj, args);} catch (Exception e) {String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";throw convertReflectionExceptionToUnchecked(msg, e);}}/*** 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.* 如向上转型到Object仍无法找到, 返回null.*/public static Field getAccessibleField(final Object obj, final String fieldName) {// 为空不报错。直接返回 nullif (obj == null) {return null;}Validate.notBlank(fieldName, "fieldName can't be blank");for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {try {Field field = superClass.getDeclaredField(fieldName);makeAccessible(field);return field;} catch (NoSuchFieldException e) {continue;}}return null;}/*** 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.* 如向上转型到Object仍无法找到, 返回null.* 匹配函数名+参数类型。* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)*/public static Method getAccessibleMethod(final Object obj, final String methodName,final Class<?>... parameterTypes) {// 为空不报错。直接返回 nullif (obj == null) {return null;}Validate.notBlank(methodName, "methodName can't be blank");for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {try {Method method = searchType.getDeclaredMethod(methodName, parameterTypes);makeAccessible(method);return method;} catch (NoSuchMethodException e) {continue;}}return null;}/*** 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.* 如向上转型到Object仍无法找到, 返回null.* 只匹配函数名。* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)*/public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) {// 为空不报错。直接返回 nullif (obj == null) {return null;}Validate.notBlank(methodName, "methodName can't be blank");for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {Method[] methods = searchType.getDeclaredMethods();for (Method method : methods) {if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) {makeAccessible(method);return method;}}}return null;}/*** 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。*/public static void makeAccessible(Method method) {if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))&& !method.isAccessible()) {method.setAccessible(true);}}/*** 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。*/public static void makeAccessible(Field field) {if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {field.setAccessible(true);}}/*** 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处* 如无法找到, 返回Object.class.*/@SuppressWarnings("unchecked")public static <T> Class<T> getClassGenricType(final Class clazz) {return getClassGenricType(clazz, 0);}/*** 通过反射, 获得Class定义中声明的父类的泛型参数的类型.* 如无法找到, 返回Object.class.*/public static Class getClassGenricType(final Class clazz, final int index) {Type genType = clazz.getGenericSuperclass();if (!(genType instanceof ParameterizedType)) {logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");return Object.class;}Type[] params = ((ParameterizedType) genType).getActualTypeArguments();if (index >= params.length || index < 0) {logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "+ params.length);return Object.class;}if (!(params[index] instanceof Class)) {logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");return Object.class;}return (Class) params[index];}public static Class<?> getUserClass(Object instance) {if (instance == null) {throw new RuntimeException("Instance must not be null");}Class clazz = instance.getClass();if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {Class<?> superClass = clazz.getSuperclass();if (superClass != null && !Object.class.equals(superClass)) {return superClass;}}return clazz;}/*** 将反射时的checked exception转换为unchecked exception.*/public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) {if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException|| e instanceof NoSuchMethodException) {return new IllegalArgumentException(msg, e);} else if (e instanceof InvocationTargetException) {return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());}return new RuntimeException(msg, e);}}## 二,ExcelUtil 工具类/*** Excel相关处理** @author*/public class ExcelUtil<T> {private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);/*** Excel sheet最大行数,默认65536*/public static final int sheetSize = 65536;/*** 工作表名称*/private String sheetName;/*** 导出类型(EXPORT:导出数据;IMPORT:导入模板)*/private Type type;/*** 工作薄对象*/private Workbook wb;/*** 工作表对象*/private Sheet sheet;/*** 样式列表*/private Map<String, CellStyle> styles;/*** 导入导出数据列表*/private List<T> list;/*** 注解列表*/private List<Object[]> fields;/*** 实体对象*/public Class<T> clazz;public ExcelUtil(Class<T> clazz) {this.clazz = clazz;}public void init(List<T> list, String sheetName, Type type) {if (list == null) {list = new ArrayList<T>();}this.list = list;this.sheetName = sheetName;this.type = type;createExcelField();createWorkbook();}/*** 对excel表单默认第一个索引名转换成list** @param is 输入流* @return 转换后集合*/public List<T> importExcel(InputStream is) throws Exception {return importExcel(StringUtil.EMPTY, is);}/*** 对excel表单指定表格索引名转换成list** @param sheetName 表格索引名* @param is 输入流* @return 转换后集合*/public List<T> importExcel(String sheetName, InputStream is) throws Exception {this.type = Type.IMPORT;this.wb = WorkbookFactory.create(is);List<T> list = new ArrayList<T>();Sheet sheet = null;if (StringUtil.isNotEmpty(sheetName)) {// 如果指定sheet名,则取指定sheet中的内容.sheet = wb.getSheet(sheetName);} else {// 如果传入的sheet名不存在则默认指向第1个sheet.sheet = wb.getSheetAt(0);}if (sheet == null) {throw new IOException("文件sheet不存在");}int rows = sheet.getPhysicalNumberOfRows();if (rows > 0) {// 定义一个map用于存放excel列的序号和field.Map<String, Integer> cellMap = new HashMap<String, Integer>();// 获取表头Row heard = sheet.getRow(0);for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) {Cell cell = heard.getCell(i);if (StringUtil.isNotEmpty(cell)) {String value = this.getCellValue(heard, i).toString();cellMap.put(value, i);} else {cellMap.put(null, i);}}// 有数据时才处理 得到类的所有field.Field[] allFields = clazz.getDeclaredFields();// 定义一个map用于存放列的序号和field.Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>();for (int col = 0; col < allFields.length; col++) {Field field = allFields[col];Excel attr = field.getAnnotation(Excel.class);if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {// 设置类的私有字段属性可访问.field.setAccessible(true);Integer column = cellMap.get(attr.name());fieldsMap.put(column, field);}}for (int i = 1; i < rows; i++) {// 从第2行开始取数据,默认第一行是表头.Row row = sheet.getRow(i);T entity = null;for (Map.Entry<Integer, Field> entry : fieldsMap.entrySet()) {Object val = this.getCellValue(row, entry.getKey());// 如果不存在实例则新建.entity = (entity == null ? clazz.newInstance() : entity);// 从map中得到对应列的field.Field field = fieldsMap.get(entry.getKey());// 取得类型,并根据对象类型设置值.Class<?> fieldType = field.getType();if (String.class == fieldType) {String s = Convert.toStr(val);if (StringUtil.endsWith(s, ".0")) {val = StringUtil.substringBefore(s, ".0");} else {val = Convert.toStr(val);}} else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) {val = Convert.toInt(val);} else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) {val = Convert.toLong(val);} else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) {val = Convert.toDouble(val);} else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) {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) {val = DateUtil.getJavaDate((Double) val);}}if (StringUtil.isNotEmpty(fieldType)) {Excel attr = field.getAnnotation(Excel.class);String propertyName = field.getName();if (StringUtil.isNotEmpty(attr.targetAttr())) {propertyName = field.getName() + "." + attr.targetAttr();} else if (StringUtil.isNotEmpty(attr.readConverterExp())) {val = reverseByExp(String.valueOf(val), attr.readConverterExp());}ReflectUtils.invokeSetter(entity, propertyName, val);}}list.add(entity);}}return list;}/*** 对list数据源将其里面的数据导入到excel表单** @param list导出数据集合* @param sheetName 工作表的名称* @return 结果*/public ResultMsg exportExcel(List<T> list, String sheetName) {this.init(list, sheetName, Type.EXPORT);return exportExcel();}/*** 对list数据源将其里面的数据导入到excel表单** @param sheetName 工作表的名称* @return 结果*/public ResultMsg importTemplateExcel(String sheetName) {this.init(null, sheetName, Type.IMPORT);return exportExcel();}/*** 对list数据源将其里面的数据导入到excel表单** @return 结果*/public ResultMsg exportExcel() {OutputStream out = null;try {// 取出一共有多少个sheet.double sheetNo = Math.ceil(list.size() / sheetSize);for (int index = 0; index <= sheetNo; index++) {createSheet(sheetNo, index);// 产生一行Row row = sheet.createRow(0);int column = 0;// 写入各个字段的列头名称for (Object[] os : fields) {Excel excel = (Excel) os[1];this.createCell(excel, row, column++);}if (Type.EXPORT.equals(type)) {fillExcelData(index, row);}}String filename = encodingFilename(sheetName);out = new FileOutputStream(getAbsoluteFile(filename));wb.write(out);return ResultMsg.success(filename);} catch (Exception e) {log.error("导出Excel异常{}", e.getMessage());throw new BaseException("导出Excel失败,请联系网站管理员!");} finally {if (wb != null) {try {wb.close();} catch (IOException e1) {e1.printStackTrace();}}if (out != null) {try {out.close();} catch (IOException e1) {e1.printStackTrace();}}}}/*** 填充excel数据** @param index 序号* @param row 单元格行*/public void fillExcelData(int index, Row row) {int startNo = index * sheetSize;int endNo = Math.min(startNo + sheetSize, list.size());for (int i = startNo; i < endNo; i++) {row = sheet.createRow(i + 1 - startNo);// 得到导出对象.T vo = (T) list.get(i);int column = 0;for (Object[] os : fields) {Field field = (Field) os[0];Excel excel = (Excel) os[1];// 设置实体类私有属性可访问field.setAccessible(true);this.addCell(excel, row, vo, field, column++);}}}/*** 创建表格样式** @param wb 工作薄对象* @return 样式列表*/private Map<String, CellStyle> createStyles(Workbook wb) {// 写入各条记录,每条记录对应excel表中的一行Map<String, CellStyle> styles = new HashMap<String, CellStyle>();CellStyle 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);return styles;}/*** 创建单元格*/public Cell createCell(Excel attr, Row row, int column) {// 创建列Cell cell = row.createCell(column);// 写入列信息cell.setCellValue(attr.name());setDataValidation(attr, row, column);cell.setCellStyle(styles.get("header"));return cell;}/*** 设置单元格信息** @param value 单元格值* @param attr 注解相关* @param cell 单元格信息*/public void setCellVo(Object value, Excel attr, Cell cell) {if (ColumnType.STRING == attr.cellType()) {cell.setCellType(CellType.NUMERIC);cell.setCellValue(StringUtil.isEmpty(value) ? attr.defaultValue() : value + attr.suffix());} else if (ColumnType.NUMERIC == attr.cellType()) {cell.setCellType(CellType.NUMERIC);cell.setCellValue(Integer.parseInt(value + ""));}}/*** 创建表格样式*/public void setDataValidation(Excel attr, Row row, int column) {if (attr.name().indexOf("注:") >= 0) {sheet.setColumnWidth(column, 6000);} else {// 设置列宽sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));row.setHeight((short) (attr.height() * 20));}// 如果设置了提示信息则鼠标放上去提示.if (StringUtil.isNotEmpty(attr.prompt())) {// 这里默认设了2-101列提示.setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column);}// 如果设置了combo属性则本列只能选择不能输入if (bo().length > 0) {// 这里默认设了2-101列只能选择不能输入.setXSSFValidation(sheet, bo(), 1, 100, column, column);}}/*** 添加单元格*/public Cell addCell(Excel attr, Row row, T vo, Field field, int column) {Cell cell = null;try {// 设置行高row.setHeight((short) (attr.height() * 20));// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.if (attr.isExport()) {// 创建cellcell = row.createCell(column);cell.setCellStyle(styles.get("data"));// 用于读取对象中的属性Object value = getTargetValue(vo, field, attr);String dateFormat = attr.dateFormat();String readConverterExp = attr.readConverterExp();if (StringUtil.isNotEmpty(dateFormat) && StringUtil.isNotEmpty(value)) {cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));} else if (StringUtil.isNotEmpty(readConverterExp) && StringUtil.isNotEmpty(value)) {cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp));} else {// 设置列类型setCellVo(value, attr, cell);}}} catch (Exception e) {log.error("导出Excel失败{}", e);}return cell;}/*** 设置 POI XSSFSheet 单元格提示** @param sheet 表单* @param promptTitle 提示标题* @param promptContent 提示内容* @param firstRow开始行* @param endRow 结束行* @param firstCol开始列* @param endCol 结束列*/public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow,int firstCol, int endCol) {DataValidationHelper helper = sheet.getDataValidationHelper();DataValidationConstraint constraint = helper.createCustomConstraint("DD1");CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);DataValidation dataValidation = helper.createValidation(constraint, regions);dataValidation.createPromptBox(promptTitle, promptContent);dataValidation.setShowPromptBox(true);sheet.addValidationData(dataValidation);}/*** 设置某些列的值只能输入预制的数据,显示下拉框.** @param sheet 要设置的sheet.* @param textlist 下拉框显示的内容* @param firstRow 开始行* @param endRow 结束行* @param firstCol 开始列* @param endCol 结束列* @return 设置好的sheet.*/public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol) {DataValidationHelper helper = sheet.getDataValidationHelper();// 加载下拉列表内容DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist);// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);// 数据有效性对象DataValidation dataValidation = helper.createValidation(constraint, regions);// 处理Excel兼容性问题if (dataValidation instanceof XSSFDataValidation) {dataValidation.setSuppressDropDownArrow(true);dataValidation.setShowErrorBox(true);} else {dataValidation.setSuppressDropDownArrow(false);}sheet.addValidationData(dataValidation);}/*** 解析导出值 0=男,1=女,2=未知** @param propertyValue 参数值* @param converterExp 翻译注解* @return 解析后值* @throws Exception*/public static String convertByExp(String propertyValue, String converterExp) throws Exception {try {String[] convertSource = converterExp.split(",");for (String item : convertSource) {String[] itemArray = item.split("=");if (itemArray[0].equals(propertyValue)) {return itemArray[1];}}} catch (Exception e) {throw e;}return propertyValue;}/*** 反向解析值 男=0,女=1,未知=2** @param propertyValue 参数值* @param converterExp 翻译注解* @return 解析后值* @throws Exception*/public static String reverseByExp(String propertyValue, String converterExp) throws Exception {try {String[] convertSource = converterExp.split(",");for (String item : convertSource) {String[] itemArray = item.split("=");if (itemArray[1].equals(propertyValue)) {return itemArray[0];}}} catch (Exception e) {throw e;}return propertyValue;}/*** 编码文件名*/public String encodingFilename(String filename) {filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";return filename;}/*** 获取下载路径** @param filename 文件名称*/public String getAbsoluteFile(String filename) {String downloadPath = GloabConfig.getDownLoadPath() + filename;File desc = new File(downloadPath);if (!desc.getParentFile().exists()) {desc.getParentFile().mkdirs();}return downloadPath;}/*** 获取bean中的属性值** @param vo 实体对象* @param field 字段* @param excel 注解* @return 最终的属性值* @throws Exception*/private Object getTargetValue(T vo, Field field, Excel excel) throws Exception {Object o = field.get(vo);if (StringUtil.isNotEmpty(excel.targetAttr())) {String target = excel.targetAttr();if (target.indexOf(".") > -1) {String[] targets = target.split("[.]");for (String name : targets) {o = getValue(o, name);}} else {o = getValue(o, target);}}return o;}/*** 以类的属性的get方法方法形式获取值** @param o* @param name* @return value* @throws Exception*/private Object getValue(Object o, String name) throws Exception {if (StringUtil.isNotEmpty(name)) {Class<?> clazz = o.getClass();String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);Method method = clazz.getMethod(methodName);o = method.invoke(o);}return o;}/*** 得到所有定义字段*/private void createExcelField() {this.fields = new ArrayList<Object[]>();List<Field> tempFields = new ArrayList<>();tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));for (Field field : tempFields) {// 单注解if (field.isAnnotationPresent(Excel.class)) {putToField(field, field.getAnnotation(Excel.class));}// 多注解if (field.isAnnotationPresent(Excels.class)) {Excels attrs = field.getAnnotation(Excels.class);Excel[] excels = attrs.value();for (Excel excel : excels) {putToField(field, excel);}}}}/*** 放到字段集合中*/private void putToField(Field field, Excel attr) {if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {this.fields.add(new Object[]{field, attr});}}/*** 创建一个工作簿*/public void createWorkbook() {this.wb = new SXSSFWorkbook(500);}/*** 创建工作表** @param sheetNo sheet数量* @param index 序号*/public void createSheet(double sheetNo, int index) {this.sheet = wb.createSheet();this.styles = createStyles(wb);// 设置工作表的名称.if (sheetNo == 0) {wb.setSheetName(index, sheetName);} else {wb.setSheetName(index, sheetName + index);}}/*** 获取单元格值** @param row 获取的行* @param column 获取单元格列号* @return 单元格值*/public Object getCellValue(Row row, int column) {if (row == null) {return row;}Object val = "";try {Cell cell = row.getCell(column);if (StringUtil.isNotEmpty(cell)) {if (cell.getCellTypeEnum() == CellType.NUMERIC || cell.getCellTypeEnum() == CellType.FORMULA) {val = cell.getNumericCellValue();if (HSSFDateUtil.isCellDateFormatted(cell)) {val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换} else {if ((Double) val % 1 > 0) {val = new DecimalFormat("0.00").format(val);} else {val = new DecimalFormat("0").format(val);}}} else if (cell.getCellTypeEnum() == CellType.STRING) {val = cell.getStringCellValue();} else if (cell.getCellTypeEnum() == CellType.BOOLEAN) {val = cell.getBooleanCellValue();} else if (cell.getCellTypeEnum() == CellType.ERROR) {val = cell.getErrorCellValue();}}} catch (Exception e) {return val;}return val;}}
## 三,范例首先在自己所需要导出的实体类上加入对应的注解标识清楚,如下:![这里插入图片描述](https://img-/eeb6cda03b6f4d8fbdd1c28f871074fc.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_Q1NETiBATEtfMTE3,size_39,color_FFFFFF,t_70,g_se,x_16)当自己所需要导出的表格实体类标识清楚后就可以直接操作了,整个使用流程都比较简单,下面上使用范例:![在这里插入图片描述](https://img-/b3218b5463f14f5db4252f5aaa15a3dd.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_Q1NETiBATEtfMTE3,size_24,color_FFFFFF,t_70,g_se,x_16)![在这里插入图片描述](https://img-/65e0e7ecf7754f49af826463f4773cff.png)后续代码逻辑需根据自己项目业务处理,## 后续以上导入导出都是事先给一个固定的excel模板让用户下载填写,然后再根据所填写的类容导入,极大的简化了导入导出所需要的代码量,望大家采纳,不足之处指点,