当前位置: 首页 > article >正文

Spring项目中使用EasyExcel实现Excel 多 Sheet 导入导出功能(完整版)


Excel 多 Sheet 导入导出功能完整实现指南


一、环境依赖
1. Maven 依赖
<!-- EasyExcel -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>

<!-- Spring Boot Web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

二、核心工具类封装
1. Excel 导出工具类
public class ExcelExportUtils {
    
    /**
     * 动态导出多 Sheet Excel(无需模板)
     * @param sheetDataMap key: Sheet名称, value: 数据列表(需保证列表元素类型一致)
     */
    public static void exportDynamicMultiSheet(
            Map<String, List<Object>> sheetDataMap,
            String filename,
            HttpServletResponse response
    ) throws IOException {
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));

        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build()) {
            WriteCellStyle headerStyle = createHeaderStyle();
            
            for (Map.Entry<String, List<Object>> entry : sheetDataMap.entrySet()) {
                String sheetName = entry.getKey();
                List<Object> dataList = entry.getValue();
                
                if (CollectionUtils.isEmpty(dataList)) {
                    throw new IllegalArgumentException("Sheet [" + sheetName + "] 数据不能为空");
                }
                Class<?> dtoClass = dataList.get(0).getClass();

                WriteSheet writeSheet = EasyExcel.writerSheet(sheetName)
                        .head(dtoClass)
                        .headStyle(headerStyle)
                        .build();
                excelWriter.write(dataList, writeSheet);
            }
        }
    }

    private static WriteCellStyle createHeaderStyle() {
        WriteCellStyle style = new WriteCellStyle();
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setHorizontalAlignment(HorizontalAlignment.CENTER);
        WriteFont font = new WriteFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 12);
        style.setWriteFont(font);
        return style;
    }
}
2. Excel 导入工具类
public class ExcelImportUtils {
    
    /**
     * 导入多 Sheet Excel
     * @param file      上传的文件
     * @param sheetMap  key: Sheet名称, value: 该Sheet对应的DTO类
     */
    public static Map<String, List<?>> importMultiSheet(
            MultipartFile file,
            Map<String, Class<?>> sheetMap
    ) throws IOException {
        Map<String, List<?>> resultMap = new LinkedHashMap<>();

        try (ExcelReader excelReader = EasyExcel.read(file.getInputStream()).build()) {
            for (Map.Entry<String, Class<?>> entry : sheetMap.entrySet()) {
                String sheetName = entry.getKey();
                Class<?> dtoClass = entry.getValue();
                GenericSheetListener<Object> listener = new GenericSheetListener<>();
                
                ReadSheet readSheet = EasyExcel.readSheet(sheetName)
                        .head(dtoClass)
                        .registerReadListener(listener)
                        .build();
                excelReader.read(readSheet);
                
                resultMap.put(sheetName, listener.getDataList());
            }
        }
        return resultMap;
    }

    /** 通用数据监听器 */
    private static class GenericSheetListener<T> implements ReadListener<T> {
        private final List<T> dataList = new ArrayList<>();
        
        @Override
        public void invoke(T data, AnalysisContext context) {
            dataList.add(data);
        }
        
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            // 可添加日志或后处理逻辑
        }
        
        public List<T> getDataList() {
            return dataList;
        }
    }
}

三、DTO 类定义
1. 用户信息 DTO(对应 Sheet1)
public class UserDTO {
    @ExcelProperty(value = "姓名", index = 0)
    private String name;
    
    @ExcelProperty(value = "年龄", index = 1)
    private Integer age;
    
    // 必须有无参构造方法
    public UserDTO() {}
    
    // Getters & Setters
}
2. 产品信息 DTO(对应 Sheet2)
public class ProductDTO {
    @ExcelProperty(value = "产品ID", index = 0)
    private Long id;
    
    @ExcelProperty(value = "产品名称", index = 1)
    private String name;
    
    // 必须有无参构造方法
    public ProductDTO() {}
    
    // Getters & Setters
}

四、Service 层实现
@Service
public class ExcelService {
    
    /**
     * 导出 Excel
     */
    public void exportExcel(HttpServletResponse response) {
        Map<String, List<Object>> sheetDataMap = new LinkedHashMap<>();
        
        // Sheet1: 用户数据
        List<Object> users = Arrays.asList(
            new UserDTO("张三", 25),
            new UserDTO("李四", 30)
        );
        sheetDataMap.put("用户信息", users);
        
        // Sheet2: 产品数据
        List<Object> products = Arrays.asList(
            new ProductDTO(1L, "笔记本电脑"),
            new ProductDTO(2L, "智能手机")
        );
        sheetDataMap.put("产品信息", products);
        
        // 调用工具类
        ExcelExportUtils.exportDynamicMultiSheet(sheetDataMap, "export_data.xlsx", response);
    }
    
    /**
     * 导入 Excel
     */
    public void importExcel(MultipartFile file) {
        // 定义 Sheet 映射关系
        Map<String, Class<?>> sheetMap = new LinkedHashMap<>();
        sheetMap.put("用户信息", UserDTO.class);
        sheetMap.put("产品信息", ProductDTO.class);
        
        // 解析数据
        Map<String, List<?>> dataMap = ExcelImportUtils.importMultiSheet(file, sheetMap);
        
        // 处理业务逻辑
        List<UserDTO> users = (List<UserDTO>) dataMap.get("用户信息");
        List<ProductDTO> products = (List<ProductDTO>) dataMap.get("产品信息");
        // 保存到数据库...
    }
}

五、Controller 层实现
@RestController
@RequestMapping("/api/excel")
public class ExcelController {
    
    @Autowired
    private ExcelService excelService;
    
    /**
     * 导出 Excel
     */
    @GetMapping("/export")
    public void exportExcel(HttpServletResponse response) {
        excelService.exportExcel(response);
    }
    
    /**
     * 导入 Excel
     */
    @PostMapping("/import")
    public ResponseEntity<String> importExcel(@RequestParam("file") MultipartFile file) {
        try {
            excelService.importExcel(file);
            return ResponseEntity.ok("文件导入成功");
        } catch (Exception e) {
            return ResponseEntity.status(500).body("导入失败: " + e.getMessage());
        }
    }
}

六、关键概念解释
1. 监听器(ReadListener
  • 作用
    在 Excel 导入过程中,逐行读取数据并处理,避免一次性加载全量数据到内存。
  • 核心方法
    • invoke(T data, AnalysisContext context):每读取一行数据时触发。
    • doAfterAllAnalysed(AnalysisContext context):当前 Sheet 解析完成后触发。
  • 使用场景
    • 数据收集(存储到 List)。
    • 数据校验(如字段非空检查)。
    • 批量插入数据库(累积一定数量后批量操作)。
2. @ExcelProperty 注解
  • 功能:定义 Java 字段与 Excel 列的映射关系。
  • 参数
    • value:对应 Excel 列名。
    • index:指定列位置(从 0 开始)。
  • 示例
    @ExcelProperty(value = "姓名", index = 0)
    private String name;
    
3. 样式配置(WriteCellStyle
  • 用途:控制单元格样式(如字体、颜色、对齐方式)。
  • 常用配置项
    • setFillForegroundColor: 背景色。
    • setHorizontalAlignment: 水平对齐。
    • setWriteFont: 字体配置(加粗、字号)。

七、常见问题
1. 类型转换异常
  • 表现ClassCastException
  • 原因:DTO 类字段类型与 Excel 单元格数据类型不匹配。
  • 解决:使用 @DateTimeFormat 或自定义转换器。
2. 表头不匹配
  • 表现:数据未正确映射到字段。
  • 原因@ExcelPropertyvalueindex 配置错误。
  • 解决:检查 DTO 类注解与 Excel 列名是否一致。
3. 内存溢出
  • 表现:大文件解析时内存占用过高。
  • 解决:确保使用监听器模式,避免一次性加载全部数据。

八、扩展功能
1. 自定义数据转换器
public class CustomConverter implements Converter<LocalDate> {
    @Override
    public LocalDate convertToJavaData(ReadConverterContext<?> context) {
        return LocalDate.parse(context.getReadCellData().getStringValue(), DateTimeFormatter.ISO_DATE);
    }
}

// 注册转换器
EasyExcel.read(inputStream)
    .registerConverter(new CustomConverter())
    .build();
2. 复杂表头合并
WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1")
    .head(Collections.singletonList(Arrays.asList("主标题", "子标题1", "子标题2")))
    .build();

九、总结

通过本方案可实现:

  • 动态导出:无需模板,根据 DTO 类自动生成表头。
  • 高效导入:逐行解析,内存占用低。
  • 类型安全:通过泛型和注解保证数据一致性。
  • 灵活扩展:支持自定义样式、转换器、校验逻辑。

http://www.kler.cn/a/612873.html

相关文章:

  • 网络故障诊断
  • QML输入控件:Dial联动、音频均衡器的实现 (3)
  • 如何构建化工质检的体系 质检LIMS系统在化工原料生产中的应用
  • Problem E: 多态
  • SQL Server安装进度卡在 57%:Windows Update 服务异常
  • Windows下在IntelliJ IDEA 使用 Git 拉取、提交脚本出现换行符问题
  • 2025AWE观察:“无AI不家电”,但“AI”还是“AL”仍是个问题
  • vue复习1~45
  • 浅谈WebSocket-FLV
  • vsCode中的正则表达式
  • 仓储物流中UWB标签实现货物实时追踪的技术路径与品铂科技方案解析
  • unity实现图片查看器有限制的移动缩放功能
  • LabVIEW时间触发协议
  • 3.1.3 MYSQL连接池
  • HarmonyOS:UI布局屏幕适配不同机型
  • Vue3.X项目中包依赖的解析与安装出现问题如何解决?
  • “头”里有什么——HTML 元信息
  • 2025年湖南建筑安全员B证备考资料
  • 数据库基础(聚合函数 分组 排序)
  • 【数据结构】C语言实现并查集:双亲指针映射与动态连通性实现详解