SpringBoot 实现 Excel 导入导出功能的三种实现方式

在日常开发中,Excel 的导入导出是非常常见的需求,尤其在管理系统中更是必不可少的功能。本文将基于实际项目代码,介绍三种不同的 Excel 导入导出实现方式,包括使用 EasyPOI 工具、自定义 POI 操作以及结合模板的 Excel 生成方式,并对它们的优缺点进行分析对比。

一、使用 EasyPOI 实现 Excel 操作

EasyPOI 是一款基于 POI 封装的 Excel 处理工具,它简化了 Excel 的导入导出代码,使开发更加高效。

1.1 导出功能实现

java 复制代码
/**
 * 数据导出接口
 */
@GetMapping("/exportExcel")
public Result exportExcel(PersonListQuery query) {
    // 1. 获取要导出的人员数据列表
    List<PersonListVO> personListvo = personService.exportExcel(query);
    
    // 2. 设置导出参数
    ExportParams exportParams = new ExportParams("人员数据报表","人员数据");
    exportParams.setStyle(ExcelExportStylerColorImpl.class);
    
    // 3. 生成Excel工作簿
    Workbook workbook = ExcelExportUtil.exportExcel(exportParams, PersonListVO.class, personListvo);
    String fileName = System.currentTimeMillis()+".xls";
    String filePath = Paths.get(excelPath, fileName).toString();
    
    // 4. 确保存储目录存在
    File file = new File(excelPath);
    if (!file.exists()) {
        file.mkdirs();
    }
    
    try{
        // 5. 写入文件
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        workbook.write(out);
        byte[] excelBytes = out.toByteArray();
        Files.write(Paths.get(filePath), excelBytes);
        
        // 6. 关闭资源
        out.close();
        workbook.close();
    }catch (Exception e){
        e.printStackTrace();
        return Result.error("导出失败");
    }
    return Result.ok().put("data",fileName);
}

1.3 EasyPOI 方式优缺点

优点:

  • 代码简洁,开发效率高
  • 注解驱动,配置灵活
  • 支持复杂表头、图片等复杂场景
  • 自带样式美化功能

缺点:

  • 额外引入第三方依赖
  • 对于非常复杂的 Excel 模板支持不够灵活

二、基于 POI 的自定义 Excel 工具类

当需要更精细的控制 Excel 格式时,可以直接使用 Apache POI 进行自定义开发,封装成工具类使用。

java 复制代码
public class ExcelUtil {

    public static String ExpPersonInfo(List<PersonVO> info, String path){
        POIFSFileSystem fs = null;
        int headRow = 2;// 数据起始行(跳过前2行表头)
        String descfile = null;
        
        try {
            // 1. 复制模板文件
            String srcfile = path + "personInfo.xls";// 源模板文件路径
            Date date = new Date();
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
            String dateStr = format.format(date);
            descfile = dateStr + ".xls";
            FileInputStream fis = new FileInputStream(srcfile);
            FileOutputStream fos = new FileOutputStream(path+descfile);
            byte [] buffer = new byte[1024*4];
            while(fis.read(buffer) != -1){
                fos.write(buffer);
            }
            fis.close();
            fos.close();
            
            // 2. 打开新创建的文件并写入数据
            fs = new POIFSFileSystem(new FileInputStream(path + descfile));
            fos = new FileOutputStream(path + descfile);
            HSSFWorkbook wb1 = new HSSFWorkbook(fs);
            HSSFSheet sheet = wb1.getSheetAt(0);
            
            // 3. 创建单元格样式
            HSSFCellStyle style = wb1.createCellStyle();
            style.setBorderLeft(BorderStyle.THIN);
            style.setBorderRight(BorderStyle.THIN);
            style.setBorderTop(BorderStyle.THIN);
            style.setBorderBottom(BorderStyle.THIN);
            
            // 4. 写入数据
            int size = info.size();
            for(int i = 0;i < size;i++){
                int col = 0;
                PersonVO p = info.get(i);
                HSSFRow row = sheet.createRow(i+headRow);
                
                HSSFCell cell = row.createCell(col++);
                cell.setCellStyle(style);
                cell.setCellValue(p.getPersonId());
                
                cell = row.createCell(col++);
                cell.setCellStyle(style);
                cell.setCellValue(p.getCommunityName());
                
                // 其他列...
            }
            
            // 5. 保存并关闭
            wb1.write(fos);
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return descfile;
    }
}

2.1 自定义工具类优缺点

优点:

  • 完全自定义,灵活性高
  • 可以精确控制 Excel 的每一个单元格
  • 可以结合模板生成复杂格式的 Excel

缺点:

  • 代码量大,开发效率低
  • 需要手动处理样式、边框等细节
  • 需注意资源关闭,否则可能导致内存泄漏

三、基于 POI 的通用导入实现

对于 Excel 导入,也可以直接使用 POI 实现通用的解析方案,适用于各种复杂结构的 Excel。

java 复制代码
/**
 * 数据导入操作
 */
@PostMapping("/parsefile/{fileName}")
public Result parsefile(@PathVariable("fileName") String fileName, HttpSession session){
    User user = (User) session.getAttribute("user");
    POIFSFileSystem fs = null;
    HSSFWorkbook wb = null;
    
    try {
        // 1. 读取Excel文件
        String basePath = excel + fileName;
        fs = new POIFSFileSystem(new FileInputStream(basePath));
        wb = new HSSFWorkbook(fs);
    } catch (Exception e) {
        e.printStackTrace();
    }
    
    // 2. 解析Excel内容到二维数组
    HSSFSheet sheet = wb.getSheetAt(0);
    Object[][] data = null;
    int r = sheet.getLastRowNum()+1;
    int c = sheet.getRow(0).getLastCellNum();
    int headRow = 2;
    data = new Object[r - headRow][c];
    
    for (int i = headRow; i < r; i++) {
        HSSFRow row = sheet.getRow(i);
        for (int j = 0; j < c; j++) {
            HSSFCell cell = null;
            try {
                cell = row.getCell(j);
                DataFormatter dataFormater = new DataFormatter();
                String a = dataFormater.formatCellValue(cell);
                data[i - headRow][j] = a;
            } catch (Exception e) {
                data[i-headRow][j] = "";
                // 特殊处理第一列
                if(j==0){
                    try {
                        double d = cell.getNumericCellValue();
                        data[i - headRow][j] = (int)d + "";
                    }catch(Exception ex){
                        data[i-headRow][j] = "";
                    }
                }
            }
        }
    }
    
    // 3. 处理解析后的数据并保存到数据库
    int row = data.length;
    String errinfo = "";
    headRow = 3;
    
    for (int i = 0; i < row; i++) {
        Person single = new Person();
        single.setPersonId(0);
        single.setState(1);
        single.setFaceUrl("");
        
        try {
            int col=1;
            String communityName = data[i][col++].toString();
            QueryWrapper<Community> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("community_name", communityName);
            Community community = this.communityService.getOne(queryWrapper);
            
            if( community == null){
                errinfo += "Excel文件第" + (i + headRow) + "行小区名称不存在!";
                return Result.ok().put("status", "fail").put("data", errinfo);
            }
            
            single.setCommunityId(community.getCommunityId());
            single.setTermName(data[i][col++].toString());
            single.setHouseNo(data[i][col++].toString());
            single.setUserName(data[i][col++].toString());
            // 设置其他字段...
            
            this.personService.save(single);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    return Result.ok().put("status", "success").put("data","数据导入完成!");
}

3.1 通用 POI 导入优缺点

优点:

  • 可以处理各种复杂格式的 Excel
  • 对 Excel 结构的控制更加精细
  • 适合导入格式不固定的场景

缺点:

  • 代码复杂,需要处理各种异常情况
  • 解析逻辑与业务逻辑混合,可读性差
  • 开发和维护成本高

四、三种方式的对比与选择建议

实现方式 开发效率 灵活性 适用场景 依赖
EasyPOI 高 中 大部分常规导入导出场景 需引入 EasyPOI 依赖
自定义 POI 工具类 中 高 需要精确控制 Excel 格式,结合模板 仅需 POI 依赖
通用 POI 导入 低 高 复杂 Excel 结构,格式不固定 仅需 POI 依赖

选择建议:

  1. 对于大多数常规的 Excel 导入导出需求,优先选择 EasyPOI,开发效率最高
  2. 当需要使用模板生成复杂格式 Excel 时,可以采用自定义 POI 工具类的方式
  3. 当 Excel 格式复杂且不固定,或者有特殊解析需求时,才考虑使用通用 POI 导入方式
  4. 导入导出功能较多的项目,建议封装统一的工具类或服务,避免代码重复

五、文件上传通用处理

无论采用哪种导入方式,都需要先处理文件上传,以下是一个通用的文件上传实现:

java 复制代码
@PostMapping("/excelUpload")
public Result excelUpload(@RequestParam("uploadExcel") MultipartFile file) throws Exception {
    if(file.getOriginalFilename().equals("")){
        return Result.error("没有选中要上传的文件");
    }else {
        // 生成唯一文件名
        String picName = UUID.randomUUID().toString();
        String oriName = file.getOriginalFilename();
        String extName = oriName.substring(oriName.lastIndexOf("."));
        String newFileName = picName + extName;
        
        // 保存文件
        File targetFile = new File(excel, newFileName);
        file.transferTo(targetFile);
        
        return Result.ok().put("data",newFileName);
    }
}

六、总结

Excel 导入导出是 Java 开发中的常见功能,选择合适的实现方式可以大大提高开发效率。EasyPOI 作为封装完善的工具,能够满足大部分需求;而当遇到复杂场景时,直接使用 POI 进行自定义开发则更加灵活。在实际项目中,应根据具体需求选择合适的实现方式,同时注意代码的可维护性和资源的正确释放。