EasyExcel 上传excel实战

一 概述

        EasyExcel是阿里巴巴的一个开源项目,在官网已经有很清晰明确的讲解,并且附带了通俗易懂的代码,可以点击关于Easyexcel | Easy Excel详细阅读。本文主要在简单实现的基础上,根据真实项目实战需求做的简单扩展实现demo,很多不足望提出后续改进。

二 上传Excel文件代码

        实战内容:上传Excel文件时,可以通过监视器检测到当前行信息的上传情况,以及成功数量。同时可以讲正确和错误的数据保存返给前端展示。

1、监视器,用于实时检测上传的情况

package com.cyl.excel.upload;

import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotBlank;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;

@Slf4j
public class DataImportMonitor {
    private final AtomicInteger succeeded = new AtomicInteger(0);
    private final AtomicInteger failed = new AtomicInteger(0);
    private LocalDateTime startTime;

    public void startImport() {
        startTime = LocalDateTime.now();
    }

    public void finishImport() {
        Duration duration = Duration.between(startTime, LocalDateTime.now());
        log.info("Import finished. Duration: {}h{}m{}s",
                duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart());
    }

    public void succeeded(String code, Integer rowNumber) {
        log.info("Succeed for code {}, row {}", code, rowNumber);
        this.succeeded.incrementAndGet();
    }

    public void failed(@NotBlank String code, Integer rawRowNumber, Exception e) {
        log.warn("Failed for code {}, row {}: {}", code, rawRowNumber, e.getMessage(), e);
        this.failed.incrementAndGet();
    }

    public void logImportedCount() {
        log.info("{} documents succeeded", this.succeeded.get());
        log.info("{} documents failed", this.failed.get());
    }

    public void nonDataReceived() {
        log.warn("Empty excel file");
    }

    public void processRow(String code, Integer rawRowNumber) {
        log.info("Started for code {}, row {}", code, rawRowNumber);
    }

    public <T> void failedForBatch(List<ImportDataRow<T>> batchRows, Function<T, String> getCodeFunction, Exception e) {
        for (ImportDataRow<T> row : batchRows) {
            log.warn("Failed for code {}, row {}: {}", getCodeFunction.apply(row.getData()), row.getRawRowNumber(), e.getMessage(), e);
        }
    }

    public void validationFailed(String code, Integer rowNumber, String errorMessage) {
        log.error("Validation failed for code {}, row {}: {}", code, rowNumber, errorMessage);
        this.failed.incrementAndGet();
    }
}

2、Api层

package com.cyl.excel.upload;

import com.cyl.api.ExcelApi;
import io.lettuce.core.dynamic.annotation.Param;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@AllArgsConstructor
@Slf4j
@RestController
@RequestMapping
public class ExcelApiImpl implements ExcelApi {
    private final UserDocumentImportService userDocumentImportService;
    @PostMapping("/excel/import")
    @Override
    public void importUser(MultipartFile file, DataImportMonitor monitor) throws IOException {
        monitor.startImport();
        userDocumentImportService.importUser(file.getInputStream(), monitor);
        monitor.finishImport();
    }

}

3、导入DTO模型

package com.cyl.excel.upload;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ImportUserDto {
    @ExcelProperty("id")
    private String id;
    @ExcelProperty("name")
    private String name;
    @ExcelProperty("age")
    private String age;
    @ExcelProperty("departmentId")
    private String departmentId;
}

4、构建行数和数据对象,记录行号和行数据,可以用于展示成功与失败的行数据。

package com.cyl.excel.upload;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.LinkedList;
import java.util.List;

@Getter
@AllArgsConstructor(staticName = "of")
public class ImportDataRow<T> {
    private final Integer rawRowNumber;

    private final T data;

    public static <T> List<ImportDataRow<T>> from(List<T> allData, Integer lineNumberOffset) {
        List<ImportDataRow<T>> dataRowList = new LinkedList<>();
        for (int i = 0; i < allData.size(); i++) {
            dataRowList.add(ImportDataRow.of(lineNumberOffset + i + 1, allData.get(i)));
        }
        return dataRowList;
    }
}

5、构建ReadAllListen,得到所有的行信息,可以进行数据的额外处理

package com.cyl.excel.upload;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;

import java.util.ArrayList;
import java.util.List;

public class ReadAllListen<T> implements ReadListener<T> {
    List<T> list = new ArrayList<>();

    @Override
    public void invoke(T data, AnalysisContext context) {
        list.add(data);
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
    }

    public List<ImportDataRow<T>> getAllRow() {
        return ImportDataRow.from(list, 0);
    }
}

6、获取所有的行的service实现

package com.cyl.excel.upload;

import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.List;

@Component
public interface ParseExcelInterface<T> {
    List<ImportDataRow<T>> parseExcel(InputStream inputStream);
}
package com.cyl.excel.upload;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.support.ExcelTypeEnum;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.List;

@Component
@AllArgsConstructor
public class UserDocumentParse implements ParseExcelInterface<ImportUserDto> {
    @Override
    public List<ImportDataRow<ImportUserDto>> parseExcel(InputStream inputStream) {
        ReadAllListen<ImportUserDto> readAllListen = new ReadAllListen<>();
        try {
            EasyExcelFactory.read(inputStream, ImportUserDto.class, readAllListen)
                    .excelType(ExcelTypeEnum.XLSX)
                    .autoCloseStream(true)
                    .ignoreEmptyRow(true)
                    .autoTrim(true)
                    .sheet(0)
                    .headRowNumber(1)
                    .doRead();
        } catch (Exception e) {
            System.out.println("error");
        }
        return readAllListen.getAllRow();
    }
}

7、service实现,批量处理行数据

package com.cyl.excel.upload;

import org.springframework.stereotype.Component;

import java.io.InputStream;

@Component
public interface UserDocumentImportService {
    void importUser(InputStream inputStream, DataImportMonitor monitor);
}
package com.cyl.excel.upload;

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.List;

@Component
@AllArgsConstructor
@NoArgsConstructor
public class UserDocumentImportServiceImpl implements UserDocumentImportService {
    @Value("${data-import.batch-size: 100}")
    private int batch;
    @Autowired
    private UserDocumentParse userDocumentParse;

    @Override
    public void importUser(InputStream inputStream, DataImportMonitor monitor) {
        List<ImportDataRow<ImportUserDto>> exportUserDtos = userDocumentParse.parseExcel(inputStream);
        if (exportUserDtos.isEmpty()) {
            monitor.nonDataReceived();
            return;
        }
        Lists.partition(exportUserDtos, batch).forEach(batch -> {
            try {
                importForBatch(batch, monitor);
            } catch (Exception e) {
               monitor.failedForBatch(batch, ImportUserDto::getId, e);
            }
        });
        monitor.logImportedCount();
    }

    private void importForBatch(List<ImportDataRow<ImportUserDto>> dataRowList, DataImportMonitor monitor) {
        dataRowList.forEach(row -> {
            monitor.processRow(row.getData().getId(), row.getRawRowNumber());
            try {
                valid();
                monitor.succeeded(row.getData().getId(), row.getRawRowNumber());
            } catch (Exception e) {
                monitor.failed(row.getData().getId(), row.getRawRowNumber(), e);
            }
        });
        save();
    }

    private void save() {
        System.out.println("保存数据库!!");
    }

    private void valid() {
        System.out.println("校验数据!!");
    }

}
是的,Spring Boot 可以使用 EasyExcel 库来上传 Excel 文件。EasyExcel 是一个基于 Java 的简单、高效的 Excel 处理工具,支持读写 Excel 文件,并且速度非常快。以下是使用 Spring BootEasyExcel 实现上传 Excel 文件的步骤: 1. 添加 EasyExcel 依赖 在 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.6</version> </dependency> ``` 2. 创建 Excel 文件上传接口 在 Spring Boot 中创建一个接口,用于上传 Excel 文件。例如: ```java @PostMapping("/upload") public String upload(MultipartFile file) throws IOException { InputStream inputStream = file.getInputStream(); EasyExcel.read(inputStream, DemoData.class, new DemoDataListener()).sheet().doRead(); return "success"; } ``` 3. 创建数据实体类 在 Spring Boot 中创建一个数据实体类,用于存储 Excel 文件中的数据。例如: ```java @Data @NoArgsConstructor @AllArgsConstructor public class DemoData { @ExcelProperty(value = "字符串标题", index = 0) private String string; @ExcelProperty(value = "日期标题", index = 1) private Date date; @ExcelProperty(value = "数字标题", index = 2) private Double doubleData; } ``` 4. 创建数据监听器类 在 Spring Boot 中创建一个数据监听器类,用于处理 Excel 文件中的数据。例如: ```java public class DemoDataListener extends AnalysisEventListener<DemoData> { private List<DemoData> list = new ArrayList<>(); @Override public void invoke(DemoData data, AnalysisContext context) { list.add(data); } @Override public void doAfterAllAnalysed(AnalysisContext context) { // 处理数据 System.out.println(list); } } ``` 5. 创建 Excel 模板文件 在 Spring Boot 中创建一个 Excel 模板文件,用于上传数据。例如: | 字符串标题 | 日期标题 | 数字标题 | | ---------- | ---------- | -------- | | Hello | 2022/01/01 | 1.1 | | World | 2022/01/02 | 2.2 | 6. 启动应用程序并测试 启动 Spring Boot 应用程序并测试上传 Excel 文件功能。可以使用 Postman 等工具进行测试。 以上就是使用 Spring BootEasyExcel 实现上传 Excel 文件的步骤。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值