听闻少年二字,当与平庸相斥。这篇文章主要讲述优雅的实现 Excel 导入导出相关的知识,希望能为你提供帮助。
?
日常在做后台系统的时候会很频繁的遇到Excel导入导出的问题,正好这次在做一个后台系统,就想着写一个公用工具来进行Excel的导入导出。
一般我们在导出的时候都是导出的前端表格,而前端表格同时也会对应的在后台有一个映射类。
所以在写这个工具的时候我们先理一下我们需要实现的效果:
- 导出方法接收一个list集合,和一个Class类型,和HttpServletResponse 对象
- 导出是可能会有下拉列表,所以需要一个map存储下拉列表数据源,传入参数后只需一行代码即可导出
- 导入方法需要传入file文件,以及一个Class类型,导入之后将会返回一个list集合,里面的对象就是传入类型的对象,传入参数后只需一行代码即可导入
首先需要创建三个注解
一个是EnableExport ,必须有这个注解才能导出
/*** 设置允许导出*/ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface EnableExport {String fileName(); }
然后就是EnableExportField,有这个注解的字段才会导出到Excel里面,并且可以设置列宽
/*** 设置该字段允许导出* 并且可以设置宽度*/ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface EnableExportField {int colWidth() default100; String colName(); }
再就是ImportIndex,导入的时候设置Excel中的列对应的序号
/*** 导入时索引*/ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ImportIndex {int index() ; }
注解使用示例
文章图片
三个注解创建好之后就需要开始操作Excel了
首先,导入方法。在后台接收到前端上传的Excel文件之后,使用poi来读取Excel文件
我们根据传入的类型上面的字段注解的顺序来分别为不同的字段赋值,然后存入集合中,再返回
代码如下:
/*** 将Excel转换为对象集合* @param excel Excel 文件* @param clazz pojo类型* @return*/ public static List< Object> parseExcelToList(File excel,Class clazz){List< Object> res = new ArrayList< > (); // 创建输入流,读取ExcelInputStream is = null; Sheet sheet = null; try {is = new FileInputStream(excel.getAbsolutePath()); if (is != null) {Workbook workbook = WorkbookFactory.create(is); //默认只获取第一个工作表sheet = workbook.getSheetAt(0); if (sheet != null) {//前两行是标题int i = 2; String values[] ; Row row = sheet.getRow(i); while (row != null) {//获取单元格数目int cellNum = row.getPhysicalNumberOfCells(); values = new String[cellNum]; for (int j = 0; j < = cellNum; j++) {Cell cell =row.getCell(j); if (cell != null) {//设置单元格内容类型cell.setCellType(Cell.CELL_TYPE_STRING ); //获取单元格值String value = https://www.songbingjia.com/android/cell.getStringCellValue() == null ? null : cell.getStringCellValue(); values[j]=value; }}Field[] fields = clazz.getDeclaredFields(); Object obj = clazz.newInstance(); for(Field f : fields){if(f.isAnnotationPresent(ImportIndex.class)){ImportIndex annotation = f.getDeclaredAnnotation(ImportIndex.class); int index = annotation.index(); f.setAccessible(true); //此处使用了阿里巴巴的fastjson包里面的一个类型转换工具类Object val =TypeUtils.cast(values[index],f.getType(),null); f.set(obj,val); }}res.add(obj); i++; row=sheet.getRow(i); }}}} catch (Exception e) {e.printStackTrace(); }return res; }
接下来就是导出方法。
导出分为几个步骤:
- 建立一个工作簿,也就是类型新建一个Excel文件
文章图片
- 建立一张sheet表
文章图片
- 设置标的行高和列宽
文章图片
- 绘制标题和表头
文章图片
这两个方法是自定义方法,代码会贴在后面
- 写入数据到Excel
文章图片
- 创建下拉列表
文章图片
- 写入文件到response
文章图片
到这里导出工作就完成了
下面是一些自定义方法的代码
/*** 获取一个基本的带边框的单元格* @param workbook* @return*/ private static HSSFCellStyle getBasicCellStyle(HSSFWorkbook workbook){HSSFCellStyle hssfcellstyle = workbook.createCellStyle(); hssfcellstyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); hssfcellstyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); hssfcellstyle.setBorderRight(HSSFCellStyle.BORDER_THIN); hssfcellstyle.setBorderTop(HSSFCellStyle.BORDER_THIN); hssfcellstyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); hssfcellstyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); hssfcellstyle.setWrapText(true); return hssfcellstyle; }/*** 获取带有背景色的标题单元格* @param workbook* @return*/ private static HSSFCellStyle getTitleCellStyle(HSSFWorkbook workbook){HSSFCellStyle hssfcellstyle =getBasicCellStyle(workbook); hssfcellstyle.setFillForegroundColor((short) HSSFColor.CORNFLOWER_BLUE.index); // 设置背景色hssfcellstyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); return hssfcellstyle; }/*** 创建一个跨列的标题行* @param workbook* @param hssfRow* @param hssfcell* @param hssfsheet* @param allColNum* @param title*/ private static void createTitle(HSSFWorkbook workbook, HSSFRow hssfRow , HSSFCell hssfcell, HSSFSheet hssfsheet,int allColNum,String title){//在sheet里增加合并单元格CellRangeAddress cra = new CellRangeAddress(0, 0, 0, allColNum); hssfsheet.addMergedRegion(cra); // 使用RegionUtil类为合并后的单元格添加边框RegionUtil.setBorderBottom(1, cra, hssfsheet, workbook); // 下边框RegionUtil.setBorderLeft(1, cra, hssfsheet, workbook); // 左边框RegionUtil.setBorderRight(1, cra, hssfsheet, workbook); // 有边框RegionUtil.setBorderTop(1, cra, hssfsheet, workbook); // 上边框//设置表头hssfRow = hssfsheet.getRow(0); hssfcell = hssfRow.getCell(0); hssfcell.setCellStyle( getTitleCellStyle(workbook)); hssfcell.setCellType(HSSFCell.CELL_TYPE_STRING); hssfcell.setCellValue(title); }/*** 设置表头标题栏以及表格高度* @param workbook* @param hssfRow* @param hssfcell* @param hssfsheet* @param colNames*/ private static void createHeadRow(HSSFWorkbook workbook,HSSFRow hssfRow , HSSFCell hssfcell,HSSFSheet hssfsheet,List< String> colNames){//插入标题行hssfRow = hssfsheet.createRow(1); for (int i = 0; i < colNames.size(); i++) {hssfcell = hssfRow.createCell(i); hssfcell.setCellStyle(getTitleCellStyle(workbook)); hssfcell.setCellType(HSSFCell.CELL_TYPE_STRING); hssfcell.setCellValue(colNames.get(i)); } } /*** excel添加下拉数据校验* @param sheet 哪个 sheet 页添加校验* @return*/ public static void createDataValidation(Sheet sheet,Map< Integer,String[]> selectListMap) {if(selectListMap!=null) {selectListMap.forEach(// 第几列校验(0开始)key 数据源数组value(key, value) -> {if(value.length> 0) {CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(2, 65535, key, key); DataValidationHelper helper = sheet.getDataValidationHelper(); DataValidationConstraint constraint = helper.createExplicitListConstraint(value); DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList); //处理Excel兼容性问题if (dataValidation instanceof XSSFDataValidation) {dataValidation.setSuppressDropDownArrow(true); dataValidation.setShowErrorBox(true); } else {dataValidation.setSuppressDropDownArrow(false); }dataValidation.setEmptyCellAllowed(true); dataValidation.setShowPromptBox(true); dataValidation.createPromptBox("提示", "只能选择下拉框里面的数据"); sheet.addValidationData(dataValidation); }}); } }
使用实例
导出数据
文章图片
导入数据(返回对象List)
【优雅的实现 Excel 导入导出】
文章图片
源码地址:
https://github.com/xyz0101/excelutils
文章图片
?
想要实时关注更多干货好文,扫描下图关注:
文章图片
推荐阅读
- Spring 官方批处理框架真香!Spring 全家桶永远滴神!
- 笑出腹肌!有些程序员真会玩代码注释
- 解决Connecting to github.com 超时的问题
- 8年Java老鸟讲解, 事务的隔离级别,这篇很通透
- 详细讲解Spring中的@Bean注解
- 程序员常犯的这些错误,你中招了没()
- 别慌,在Java面试的时候,面试官会这样问关于框架的问题()
- 用FastDFS一步步搭建文件管理系统
- Python 环境搭建