注解+反射优雅的实现Excel导入导出(通用版),飘了!

December 17, 2023
测试
测试
测试
测试
12 分钟阅读

点击上方“芋道源码”,选择“设为星标”

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

  • 原创 | Java 2021 超神之路,很肝~
  • 中文详细注释的开源项目
  • RPC 框架 Dubbo 源码解析
  • 网络应用框架 Netty 源码解析
  • 消息中间件 RocketMQ 源码解析
  • 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析
  • 作业调度中间件 Elastic-Job 源码解析
  • 分布式事务中间件 TCC-Transaction 源码解析
  • Eureka 和 Hystrix 源码解析
  • Java 并发源码

来源:blog.csdn.net/youzi1394046585/

article/details/86670203

  • 实现过程:
  • 三个注解创建好之后就需要开始操作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() default  100;
     String colName();
}

再就是ImportIndex,导入的时候设置Excel中的列对应的序号

/**
 * 导入时索引
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ImportIndex {
     int index() ;

}

注解使用示例

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

三个注解创建好之后就需要开始操作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<>();
    // 创建输入流,读取Excel
    InputStream 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 = 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;
}

接下来就是导出方法。

导出分为几个步骤:

  1. 建立一个工作簿,也就是类型新建一个Excel文件
  1. 建立一张sheet表
  1. 设置标的行高和列宽
  1. 绘制标题和表头

这两个方法是自定义方法,代码会贴在后面

  1. 写入数据到Excel
  1. 创建下拉列表
  1. 写入文件到response

到这里导出工作就完成了

基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。 项目地址:https://github.com/YunaiV/onemall

下面是一些自定义方法的代码

/**
 * 获取一个基本的带边框的单元格
 * @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)

源码地址:

https://github.com/xyz0101/excelutils



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。谢谢支持哟 (*^__^*)

继续阅读

更多来自我们博客的帖子

如何安装 BuddyPress
由 测试 December 17, 2023
经过差不多一年的开发,BuddyPress 这个基于 WordPress Mu 的 SNS 插件正式版终于发布了。BuddyPress...
阅读更多
Filter如何工作
由 测试 December 17, 2023
在 web.xml...
阅读更多
如何理解CGAffineTransform
由 测试 December 17, 2023
CGAffineTransform A structure for holding an affine transformation matrix. ...
阅读更多