excel导入导出

1. 导入依赖

xml 复制代码
<dependency>
    <groupId>com.alibaba.easyexcel</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.0.5</version>
</dependency>

2. 定义实体类

java 复制代码
@Data
@ColumnWidth(25)
@HeadRowHeight(20)
@ContentRowHeight(18)
public class ImportConfigDTO {

    @ExcelProperty("类型")
    @ColumnWidth(15)
    private String type;

    @ExcelProperty("故障名")
    @ColumnWidth(15)
    private String name;

    @ExcelProperty("故障码")
    @ColumnWidth(15)
    private String faultCode;

    @ExcelProperty("描述")
    @ColumnWidth(15)
    private String description;
}

3. Controller类

java 复制代码
@Slf4j
@RestController
public class ExportConfigController {

    @Autowired
    private ImportConfigService importConfigService;

    @PostMapping("/api/v1/importConfigTemplate")
    public ResponseEntity<String> importConfigTemplate(MultipartFile file) {
        importDtcConfigService.importDtoConfigTemplate(file);
        return ResponseEntity.ok("success");
    }

    @PostMapping("/api/v1/import-config")
    public ResponseEntity<String> importConfig(MultipartFile file) {
        String filename = file.getOriginalFilename();
        if (StringUtil.isBlank(filename)) {
            throw new RuntimeException("请上传文件!");
        }
        if ((!StringUtils.endsWithIgnoreCase(filename, ".xls") && !StringUtils.endsWithIgnoreCase(filename, ".xlsx"))) {
            throw new RuntimeException("请上传正确的excel文件!");
        }
        InputStream inputStream;
        try {
            ConfigImportListener importListener = new ConfigImportListener(importConfigService);
            inputStream = new BufferedInputStream(file.getInputStream());
            ExcelReaderBuilder builder = EasyExcel.read(inputStream, ImportConfigDTO.class, importListener);
            builder.doReadAll();
        } catch (IOException e) {
            throw new RuntimeException("excel文件上传出现错误" + e.getMessage());
        }
        return ResponseEntity.ok("操作成功");
    }

    @SneakyThrows
    @GetMapping("/api/v1/export-config")
    public void exportConfig(HttpServletResponse response) {
        List<ImportConfigDTO> list = importConfigService.findAll();
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        String fileName = URLEncoder.encode("配置数据导出", StandardCharsets.UTF_8);
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), ImportConfigDTO.class).sheet("配置数据表").doWrite(list);
    }

    @SneakyThrows
    @GetMapping("/api/v1/export-template")
    public void exportTemplate(HttpServletResponse response) {
        List<ImportConfigDTO> list = new ArrayList<>();
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        String fileName = URLEncoder.encode("配置数据模板", StandardCharsets.UTF_8);
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), ImportConfigDTO.class).sheet("配置数据表").doWrite(list);
    }
}

4. 两种监听类定义方式

  • 方式一:调用保存方法
java 复制代码
@Data
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class ConfigImportListener extends AnalysisEventListener<ImportConfigDTO> {

	/**
	 * 默认每隔3000条存储数据库
	 */
	private int batchCount = 3000;
	/**
	 * 缓存的数据列表
	 */
	private List<ImportConfigDTO> list = new ArrayList<>();

	private final ImportConfigService importConfigService;

	@Override
	public void invoke(ImportConfigDTO data, AnalysisContext context) {
		list.add(data);
		// 达到BATCH_COUNT,则调用importer方法入库,防止数据几万条数据在内存,容易OOM
		if (list.size() >= batchCount) {
			// 调用importer方法
			importConfigService.uploadExcelDtoConfig(list);
			// 存储完成清理list
			list.clear();
		}
	}

	@Override
	public void doAfterAllAnalysed(AnalysisContext analysisContext) {
		// 调用importer方法
		importConfigService.uploadExcelDtoConfig(list);
		// 存储完成清理list
		list.clear();
	}

}
  • 方式二:只做数据接收
java 复制代码
@Data
@EqualsAndHashCode(callSuper = true)
public class ConfigListener extends AnalysisEventListener<ImportConfigDTO> {

    List<ImportConfigDTO> list = Lists.newLinkedList();

    List<ExcelDataConvertException> listException = new ArrayList<>();

    Map<Integer, String> excelHeadMap = new HashMap<>(10);

    @Override
    public void invoke(ImportConfigDTO data, AnalysisContext analysisContext) {
        list.add(data);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        excelHeadMap = headMap;
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) {
    }
}

5. Excel工具类

java 复制代码
@Slf4j
public class ExcelUtils {

    public static final long MAX_SIZE = 10 * 1024 * 1024;


    /**
     * 用于读取excel
     * @param file                  文件参数
     * @param importClass           导入对象class
     * @param analysisEventListener 分析事件监听器
     */
    public static void readExcel(MultipartFile file, Class<?> importClass, AnalysisEventListener<?> analysisEventListener) {
        // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
        try(InputStream inputStream = file.getInputStream()) {
            if (file.getSize() > MAX_SIZE) {
                throw new RuntimeException("The maximum file size allowed for upload is 10MB 上传文件最大允许上传10MB");
            }
            String fileName = file.getOriginalFilename();
            if (fileName == null || !(fileName.endsWith(".xls") || fileName.endsWith(".xlsx"))) {
                throw new RuntimeException("The uploaded file format is incorrect, it must be in. xls or. xlsx format 上传文件格式不正确,必须是.xls或.xlsx格式");
            }
            EasyExcel.read(inputStream, importClass, analysisEventListener).sheet().doRead();
        } catch (IOException e) {
            throw new RuntimeException("excel文件上传出现错误" + e.getMessage());
        }
    }
}

6. Service类

java 复制代码
@Slf4j
@Service
public class ImportConfigService {

    @Autowired
    private ConfigMapper configMapper;

    public void importDtoConfigTemplate(MultipartFile multipartFile) {
        ConfigListener configListener = new ConfigListener();
        //读取excel
        ExcelUtils.readExcel(multipartFile, ImportConfigDTO.class, configListener);

        List<ImportConfigDTO> configImportList = configListener.getList();

        if (CollectionUtil.isEmpty(configImportList)) {
            throw new RuntimeException("Upload file is empty 上传文件为空");
        }
        List<ExcelDataConvertException> listException = configListener.getListException();
        Set<String> infoSet = listException.stream()
                .map(e -> "第" + (e.getRowIndex() + 1) + "行,第" + (e.getColumnIndex() + 1) + "列")
                .collect(Collectors.toSet());
        if (CollectionUtil.isEmpty(infoSet)) {
            uploadExcelDtoConfig(configImportList);
        } else {
            throw new RuntimeException(String.join(";", infoSet));
        }
    }

    public void uploadExcelDtoConfig(List<ImportConfigDTO> importConfigDTOList) {
        List<ConfigPO> configPOList = new ArrayList<>();
        for (ImportConfigDTO importConfigDTO : importConfigDTOList) {
            ConfigPO configPO = new ConfigPO();
            configPO.setId(IdWorker.nextId());
            configPO.setType(importConfigDTO.getType());
            configPO.setName(importConfigDTO.getName());
            configPO.setFaultCode(importConfigDTO.getFaultCode());
            configPO.setDescription(importConfigDTO.getDescription());
            configPO.setCreatedAt(LocalDateTime.now());
            configPO.setCreatedBy("system");
            configPO.setUpdatedAt(LocalDateTime.now());
            configPO.setUpdatedBy("system");
            configPOList.add(configPO);
        }
        if (CollectionUtil.isNotEmpty(configPOList)) {
            configMapper.batchInsert(configPOList);
        }
    }

    public List<ImportConfigDTO> findAll() {
        return configMapper.findAll();
    }
}
相关推荐
Flittly2 天前
【AgentScope Java新手村系列】(16)从RAG到多路检索
java·spring boot·spring
人活一口气2 天前
从JVM调优到MCP协议:Java全栈技术体系深度总结与企业级架构实践
java·spring boot
Java陈序员3 天前
企业级!一个基于 Java 开发的开源 AI 应用开发平台!
spring boot·agent·mcp
杨运交3 天前
[041][公共模块]分布式唯一ID生成器设计与实现:一款灵活可扩展的雪花算法框架
spring boot
Flittly4 天前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
Flynt5 天前
从Spring Boot 4.0升到4.1,我在Maven和gRPC上栽了跟头
java·spring boot·后端
掉鱼的猫6 天前
Spring Boot → Solon 注解迁移实战指南:一张对照表说清楚
java·spring boot
人活一口气7 天前
Spring Boot与AIGC的完美结合:从零搭建智能内容生成平台
java·spring boot·aigc
java小白小10 天前
SpringBoot(01): 初识SpringBoot,从Spring的痛点说起
spring boot
用户31693538118310 天前
如何从零编写一个 Spring Boot Starter
spring boot