├── .idea ├── compiler.xml └── misc.xml ├── README.md ├── excelutils.iml ├── pom.xml └── src └── main └── java └── com └── jenkin └── excel ├── ExcelUtils.java ├── anno ├── EnableExport.java ├── EnableExportField.java ├── EnableSelectList.java └── ImportIndex.java └── enums └── ColorEnum.java /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # excelutils 2 | # 一个通用的Excel导入导出工具 3 | 使用ExcelUtils里面的parseExcelToList方法进行导入 4 | 使用ExcelUtils里面的exportExcel方法进行导出 5 | 6 | 以下四个注解是导入导出的实体类需要依赖的注解 7 | EnableExport 8 | 该注解使用在类上,设置允许导入导出 9 | EnableExportField 10 | 该注解设置在字段上,设置允许导出该字段,并且可以设置宽度,标题名,背景颜色 11 | EnableSelectList 12 | 该注解设置在字段上,设置是否使用下拉列表 13 | ImportIndex 14 | 该注解设置在字段上,设置允许导入,并且设置导入时对应的Excel列索引 15 | 16 | ColorEnum 17 | 该枚举是一个颜色枚举,里面有一些常用颜色 18 | 19 | 20 | 使用示例: 21 | 实体类: 22 | @EnableExport(fileName = "") 23 | public class SlrEmpSalary implements Serializable { 24 | @ImportIndex(index = 0) 25 | @EnableExportField(colName = "序号", colWidth = 80) 26 | private int seqNumber; 27 | @ImportIndex(index = 1) 28 | @EnableExportField(colName = "员工编号", colWidth = 160,cellColor = ColorEnum.RED) 29 | private String employeeCode; 30 | @ImportIndex(index = 2) 31 | @EnableExportField(colName = "员工姓名", colWidth = 160) 32 | private String employeeName;} 33 | 34 | 导入使用方法: 35 | //文件输入流 36 | inputStream =uploadedFile.getInputStream(); 37 | //获取对象集合 38 | List< SlrEmpSalary> empSalaryList = 39 | (List< SlrEmpSalary>)ExcelUtils.parseExcelToList(inputStream, SlrEmpSalary.class); 40 | 41 | 导出使用方法: 42 | //导出数据 43 | List< SlrEmpSalary> dataList = new ArrayList(); 44 | //导出 45 | FileUtils.exportExcel(outputStream, dataList, SlrEmpSalary.class, Const.ALL_SELECT_LIST_MAP, exportTitle); 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /excelutils.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.jenkin.excel 8 | excel-utils 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | com.alibaba 15 | fastjson 16 | 1.1.41 17 | 18 | 19 | 20 | org.apache.poi 21 | poi 22 | 3.8 23 | 24 | 25 | org.apache.poi 26 | poi-ooxml 27 | 3.8 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/com/jenkin/excel/ExcelUtils.java: -------------------------------------------------------------------------------- 1 | package com.jenkin.excel; 2 | 3 | import com.alibaba.fastjson.util.TypeUtils; 4 | 5 | 6 | import com.jenkin.excel.anno.EnableExport; 7 | import com.jenkin.excel.anno.EnableExportField; 8 | import com.jenkin.excel.anno.EnableSelectList; 9 | import com.jenkin.excel.anno.ImportIndex; 10 | import com.jenkin.excel.enums.ColorEnum; 11 | import org.apache.poi.hssf.usermodel.*; 12 | import org.apache.poi.ss.usermodel.*; 13 | import org.apache.poi.ss.util.CellRangeAddress; 14 | import org.apache.poi.ss.util.CellRangeAddressList; 15 | import org.apache.poi.ss.util.RegionUtil; 16 | import org.apache.poi.xssf.usermodel.XSSFDataValidation; 17 | import java.io.*; 18 | import java.lang.reflect.Field; 19 | import java.lang.reflect.InvocationTargetException; 20 | import java.lang.reflect.Method; 21 | 22 | import java.util.ArrayList; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.regex.Matcher; 27 | import java.util.regex.Pattern; 28 | import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 29 | public class ExcelUtils { 30 | /** 所有的下拉列表数据存在这个map中,key是对应的Excel列的序号,从0开始,value为下拉列表键对值 **/ 31 | public static final Map> ALL_SELECT_LIST_MAP = new HashMap> (); 32 | /** 33 | * 将Excel转换为对象集合 34 | * @param excel Excel 文件 35 | * @param clazz pojo类型 36 | * @return 37 | */ 38 | public static List parseExcelToList(File excel,Class clazz){ 39 | List res = new ArrayList(); 40 | // 创建输入流,读取Excel 41 | InputStream is = null; 42 | Sheet sheet = null; 43 | try { 44 | is = new FileInputStream(excel.getAbsolutePath()); 45 | if (is != null) { 46 | Workbook workbook = WorkbookFactory.create(is); 47 | //默认只获取第一个工作表 48 | sheet = workbook.getSheetAt(0); 49 | if (sheet != null) { 50 | int i = 2; 51 | String values[] ; 52 | Row row = sheet.getRow(i); 53 | while (row != null) { 54 | //获取单元格数目 55 | int cellNum = row.getPhysicalNumberOfCells(); 56 | values = new String[cellNum]; 57 | for (int j = 0; j <= cellNum; j++) { 58 | Cell cell = row.getCell(j); 59 | if (cell != null) { 60 | //设置单元格内容类型 61 | cell.setCellType(Cell.CELL_TYPE_STRING ); 62 | //获取单元格值 63 | String value = cell.getStringCellValue() == null ? null : cell.getStringCellValue(); 64 | values[j]=value; 65 | } 66 | } 67 | Field[] fields = clazz.getDeclaredFields(); 68 | Object obj = clazz.newInstance(); 69 | for(Field f : fields){ 70 | if(f.isAnnotationPresent(ImportIndex.class)){ 71 | ImportIndex annotation = f.getAnnotation(ImportIndex.class); 72 | int index = annotation.index(); 73 | String useSetMethodName = annotation.useSetMethodName(); 74 | if(!"".equals(useSetMethodName)){ 75 | Object val =TypeUtils.cast(values[index],f.getType(),null); 76 | f.setAccessible(true); 77 | Method method = clazz.getMethod(useSetMethodName, new Class[]{f.getType(),Object.class}); 78 | method.setAccessible(true); 79 | method.invoke(obj, new Object[]{f.get(obj),val}); 80 | }else{ 81 | f.setAccessible(true); 82 | Object val =TypeUtils.cast(values[index],f.getType(),null); 83 | f.set(obj,val); 84 | } 85 | } 86 | } 87 | res.add(obj); 88 | i++; 89 | row=sheet.getRow(i); 90 | } 91 | } 92 | } 93 | } catch (Exception e) { 94 | e.printStackTrace(); 95 | } 96 | return res; 97 | } 98 | 99 | /** 100 | * 将Excel转换为对象集合 101 | * @param excel Excel 文件输入流 102 | * @param clazz pojo类型 103 | * @return 104 | */ 105 | public static List parseExcelToList(InputStream excel,Class clazz) throws IOException, 106 | InvalidFormatException, 107 | InstantiationException, 108 | IllegalAccessException, 109 | NoSuchMethodException, 110 | InvocationTargetException { 111 | List res = new ArrayList(); 112 | // 创建输入流,读取Excel 113 | InputStream is = null; 114 | Sheet sheet = null; 115 | 116 | is = excel; 117 | if (is != null) { 118 | Workbook workbook = WorkbookFactory.create(is); 119 | //默认只获取第一个工作表 120 | sheet = workbook.getSheetAt(0); 121 | if (sheet != null) { 122 | int i = 2; 123 | String values[] ; 124 | Row row = sheet.getRow(i); 125 | while (row != null) { 126 | //获取单元格数目 127 | int cellNum = row.getPhysicalNumberOfCells(); 128 | values = new String[cellNum]; 129 | for (int j = 0; j <= cellNum; j++) { 130 | Cell cell = row.getCell(j); 131 | if (cell != null) { 132 | //设置单元格内容类型 133 | cell.setCellType(Cell.CELL_TYPE_STRING ); 134 | //获取单元格值 135 | String value = cell.getStringCellValue() == null ? null : cell.getStringCellValue(); 136 | values[j]=value; 137 | } 138 | } 139 | Field[] fields = clazz.getDeclaredFields(); 140 | Object obj = clazz.newInstance(); 141 | for(Field f : fields){ 142 | if(f.isAnnotationPresent(ImportIndex.class)){ 143 | 144 | ImportIndex annotation = f.getAnnotation(ImportIndex.class); 145 | int index = annotation.index(); 146 | Object value = values[index]; 147 | if(f.isAnnotationPresent(EnableSelectList.class)){ 148 | 149 | value = getKeyByValue(ALL_SELECT_LIST_MAP.get(index),String.valueOf(value ) ); 150 | 151 | } 152 | String useSetMethodName = annotation.useSetMethodName(); 153 | if(!"".equals(useSetMethodName)){ 154 | Object val =TypeUtils.cast(value,f.getType(),null); 155 | f.setAccessible(true); 156 | Method method = clazz.getMethod(useSetMethodName, new Class[]{f.getType(),Object.class}); 157 | method.setAccessible(true); 158 | method.invoke(obj, new Object[]{f.get(obj),val}); 159 | }else{ 160 | f.setAccessible(true); 161 | Object val =TypeUtils.cast(value,f.getType(),null); 162 | f.set(obj,val); 163 | } 164 | 165 | } 166 | } 167 | res.add(obj); 168 | i++; 169 | row=sheet.getRow(i); 170 | } 171 | } 172 | } 173 | 174 | return res; 175 | } 176 | 177 | /** 178 | * 导出 Excel 179 | * @param outputStream 输出流,用于写文件 180 | * @param dataList 需要导出的数据 181 | * @param clazz 导出数据的pojo类型 182 | * @param selectListMap 下拉列表的列 183 | * @param exportTitle 当该参数不为空则替换默认的标题 184 | */ 185 | public static void exportExcel(OutputStream outputStream, List dataList, Class clazz, Map> selectListMap,String exportTitle){ 186 | //创建一个Excel工作簿 187 | HSSFWorkbook workbook = new HSSFWorkbook(); 188 | //建立表 189 | HSSFSheet hssfsheet = workbook.createSheet(); 190 | 191 | hssfsheet.setDefaultRowHeight( ( short )(20*20) ); 192 | //检查当前pojo是否允许导出 193 | if(clazz.isAnnotationPresent(EnableExport.class)) { 194 | EnableExport export = (EnableExport) clazz.getAnnotation(EnableExport.class); 195 | //获取所有标题名称 196 | List colNames =new ArrayList(); 197 | //获取所有标题的背景颜色 198 | List colors =new ArrayList(); 199 | //所有允许导出的字段 200 | List fieldList = new ArrayList(); 201 | for(Field field : clazz.getDeclaredFields()){ 202 | if(field.isAnnotationPresent(EnableExportField.class)){ 203 | EnableExportField enableExportField = field.getAnnotation(EnableExportField.class); 204 | colNames.add(enableExportField.colName()); 205 | colors.add(enableExportField.cellColor()); 206 | fieldList.add(field); 207 | } 208 | } 209 | //设置每列的宽度 210 | for(int i=0;i colNames,List colors){ 339 | //插入标题行 340 | hssfRow = hssfsheet.createRow(1); 341 | for (int i = 0; i < colNames.size(); i++) { 342 | hssfcell = hssfRow.createCell(i); 343 | hssfcell.setCellStyle(getTitleCellStyle(workbook,colors.get(i))); 344 | hssfcell.setCellType(HSSFCell.CELL_TYPE_STRING); 345 | hssfcell.setCellValue(colNames.get(i)); 346 | } 347 | } 348 | /** 349 | * excel添加下拉数据校验 350 | * @param sheet 哪个 sheet 页添加校验 351 | * @return 352 | */ 353 | public static void createDataValidation(Sheet sheet, Map> selectListMap) { 354 | if(selectListMap!=null) { 355 | for(Map.Entry> entry:selectListMap.entrySet() ){ 356 | Integer key = entry.getKey(); 357 | Map value = entry.getValue(); 358 | // 第几列校验(0开始)key 数据源数组value 359 | if(value.size()>0) { 360 | int i=0; 361 | String[] valueArr = new String[value.size()]; 362 | for(Map.Entry ent :value.entrySet()){ 363 | valueArr[i] = ent.getValue(); 364 | i++; 365 | } 366 | CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(2, 65535, key, key); 367 | DataValidationHelper helper = sheet.getDataValidationHelper(); 368 | DataValidationConstraint constraint = helper.createExplicitListConstraint(valueArr); 369 | DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList); 370 | //处理Excel兼容性问题 371 | if (dataValidation instanceof XSSFDataValidation) { 372 | dataValidation.setSuppressDropDownArrow(true); 373 | dataValidation.setShowErrorBox(true); 374 | } else { 375 | dataValidation.setSuppressDropDownArrow(false); 376 | } 377 | dataValidation.setEmptyCellAllowed(true); 378 | dataValidation.setShowPromptBox(true); 379 | dataValidation.createPromptBox("提示", "只能选择下拉框里面的数据"); 380 | sheet.addValidationData(dataValidation); 381 | } 382 | } 383 | } 384 | } 385 | /** 386 | *通过value获取key值 387 | * @param selectMap 388 | * @param value 389 | * @return 390 | */ 391 | private static String getKeyByValue(Map selectMap,String value){ 392 | if(selectMap!=null){ 393 | for(Map.Entry ent :selectMap.entrySet()){ 394 | if(value!=null&&value.equals(ent.getValue())) 395 | return ent.getKey(); 396 | } 397 | }else{ 398 | return value; 399 | } 400 | return null; 401 | } 402 | 403 | 404 | /** 405 | *判断字符串是否为数字 406 | * @param str 407 | * @return 408 | */ 409 | private static boolean isNumeric(String str) { 410 | Pattern pattren = 411 | Pattern.compile("[-+]?(([0-9]+)([.]([0-9]+))?|([.]([0-9]+))?)$"); 412 | if (str != null && !"".equals(str.trim())) { 413 | Matcher matcher = pattren.matcher(str); 414 | if (matcher.matches()) { 415 | if (!str.contains(".") && str.startsWith("0")) { 416 | return false; 417 | } 418 | return true; 419 | } 420 | } 421 | return false; 422 | } 423 | 424 | /** 425 | *设置单元格的值 426 | * @param value 427 | * @param hssfcell 428 | * @param hssfRow 429 | * @param cellStyle 430 | * @param cellIndex 431 | */ 432 | private static void setCellValue(Object value,HSSFCell hssfcell,HSSFRow hssfRow,CellStyle cellStyle,int cellIndex) { 433 | String valueStr = String.valueOf(value); 434 | hssfcell =hssfRow.createCell(cellIndex ); 435 | //暂时认为数字类型不会有下拉列表 436 | if (isNumeric(valueStr)) { 437 | hssfcell.setCellStyle(cellStyle); 438 | hssfcell.setCellType(HSSFCell.CELL_TYPE_NUMERIC); 439 | hssfcell.setCellValue(Double.valueOf(valueStr)); 440 | } else { 441 | hssfcell.setCellStyle(cellStyle); 442 | hssfcell.setCellType(HSSFCell.CELL_TYPE_STRING); 443 | hssfcell.setCellValue(valueStr); 444 | } 445 | } 446 | } 447 | 448 | 449 | -------------------------------------------------------------------------------- /src/main/java/com/jenkin/excel/anno/EnableExport.java: -------------------------------------------------------------------------------- 1 | package com.jenkin.excel.anno; 2 | 3 | 4 | import com.jenkin.excel.enums.ColorEnum; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * 设置允许导出 13 | */ 14 | @Target(ElementType.TYPE) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface EnableExport { 17 | //设置文件名/标题 18 | String fileName(); 19 | //设置背景颜色 20 | ColorEnum cellColor() default ColorEnum.BLUE; 21 | } -------------------------------------------------------------------------------- /src/main/java/com/jenkin/excel/anno/EnableExportField.java: -------------------------------------------------------------------------------- 1 | package com.jenkin.excel.anno; 2 | 3 | 4 | import com.jenkin.excel.enums.ColorEnum; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * 设置该字段允许导出 13 | * 并且可以设置宽度 14 | * 以及设置下拉列表字段的key 用来匹配对应的label导出 15 | */ 16 | @Target(ElementType.FIELD) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | public @interface EnableExportField { 19 | //宽度 20 | int colWidth() default 100; 21 | //标题名称 22 | String colName(); 23 | //设置get方法 24 | String useGetMethod() default ""; 25 | //设置背景颜色 26 | ColorEnum cellColor() default ColorEnum.BLUE; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/jenkin/excel/anno/EnableSelectList.java: -------------------------------------------------------------------------------- 1 | package com.jenkin.excel.anno; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | /** 8 | * 允许使用下拉列表 9 | */ 10 | @Target(ElementType.FIELD) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | public @interface EnableSelectList { 13 | } -------------------------------------------------------------------------------- /src/main/java/com/jenkin/excel/anno/ImportIndex.java: -------------------------------------------------------------------------------- 1 | package com.jenkin.excel.anno; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 导入时索引 10 | * 从0开始 11 | */ 12 | @Target(ElementType.FIELD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface ImportIndex { 15 | //索引 16 | int index() ; 17 | //设置set方法 18 | String useSetMethodName() default ""; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/jenkin/excel/enums/ColorEnum.java: -------------------------------------------------------------------------------- 1 | package com.jenkin.excel.enums; 2 | 3 | import org.apache.poi.hssf.util.HSSFColor; 4 | 5 | public enum ColorEnum { 6 | RED("红色", HSSFColor.RED.index), 7 | GREEN("绿色", HSSFColor.GREEN.index), 8 | BLANK("白色", HSSFColor.WHITE.index), 9 | YELLOW("黄色", HSSFColor.YELLOW.index), 10 | BLUE("蓝色", HSSFColor.CORNFLOWER_BLUE.index); 11 | private String name; 12 | private short index; 13 | private ColorEnum( String name, short index) { 14 | this.name = name; 15 | this.index = index; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public short getIndex() { 27 | return index; 28 | } 29 | 30 | public void setIndex(short index) { 31 | this.index = index; 32 | } 33 | 34 | } 35 | --------------------------------------------------------------------------------