解析excel咱們經常使用的應該就是poi了,這個比較坑,以前項目用過一下,此次咱們使用一下阿里的easyexcel吧!easyexcel的使用很簡單,比較相似於最開始解析xml使用的SAX,就是每讀取文件一行,就直接進行處理,徹底不須要文件都加載到內存中!!!html
easyexcel的官方文檔前端
1. 隨意新建一個springboot項目,導入常見的依賴(基於springboot+mybatis+mybatisplus+lombok+swagger),而後再導入easyexcel:java
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.1</version> </dependency>
2. excel文件中的內容,好比淘寶商品的分類,確定是分爲一級,二級,三級分類等等,下圖所示,一個一級分類對應多個二級分類,一個二級對應多個三級,等等git
咱們就不弄那麼麻煩了,以一個課程的分類爲例,有一級分類二級分類,下圖的excel文件所示,咱們須要將這個excel文件上傳,而後經過easyexcel解析出來,丟到數據庫中,相似圖中數據庫表中的數據github
sql腳本以下::web
create table subject ( id char(19) not null comment '課程類別ID' primary key, title varchar(10) not null comment '類別名稱', parent_id char(19) default '0' not null comment '父ID' )comment '課程科目'; create index idx_parent_id on subject (parent_id);
3. controller代碼:spring
package com.protagonist.edu.controller; import com.protagonist.edu.service.SubjectService; import com.protagonist.responseVO.Result; import com.protagonist.responseVO.StatusCode; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; 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 javax.annotation.Resource; /** * <p> * 課程科目 前端控制器 * </p> * * @author protagonist * @since 2020-10-01 */ @Api("課程分類的api") @RestController @RequestMapping("/edu/subject") @Slf4j public class SubjectController { @Resource private SubjectService subjectServiceImpl; //添加課程分類,上傳excel而後進行讀取,解析成對象 @PostMapping("/addSubject") @ApiOperation(value = "上傳excel文件中的一級分類和二級分類") public Result addSubject(MultipartFile file) { subjectServiceImpl.saveSubject(file); return new Result<>(true, StatusCode.OK, "添加課程分類成功"); } }
4.service和serviceImpl:sql
public interface SubjectService extends IService<Subject> { /** * 添加課程分類 * @param file */ void saveSubject(MultipartFile file); } @Service @Slf4j public class SubjectServiceImpl extends ServiceImpl<SubjectMapper, Subject> implements SubjectService { @Override public void saveSubject(MultipartFile file) { try{ InputStream inputStream = file.getInputStream(); //讀取excel EasyExcel.read(inputStream, ExcelSubjectData.class,new SubjectExcelListener(this)).sheet().doRead(); }catch (Exception e){ log.error("讀取excel文件異常",e); throw new ProtagonistException(StatusCode.READ_FILE_ERROR,"讀取excel文件異常"); } } }
5.entity數據庫
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("subject") @ApiModel(value="Subject對象", description="課程科目") public class Subject implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "課程類別ID") @TableId(value = "id", type = IdType.ID_WORKER_STR) private String id; @ApiModelProperty(value = "類別名稱") private String title; @ApiModelProperty(value = "父ID") private String parentId; }
6.咱們還須要一個類,這個類的每個實例都對應excel中每一行數據,咱們將每一行數據都轉成一個對象以後,而後進行處理就方便了;json
package com.protagonist.edu.excel; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; @Data public class ExcelSubjectData { /** * 一級分類 */ @ExcelProperty(index = 0) private String oneSubjectName; /** * 二級分類 */ @ExcelProperty(index = 1) private String twoSubjectName; }
7.listener,以前說過easyexcel是每讀一行數據都會處理一下, 因此這裏須要來一個監聽器:
package com.protagonist.edu.entity.listener; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.protagonist.edu.entity.Subject; import com.protagonist.edu.excel.ExcelSubjectData; import com.protagonist.edu.service.SubjectService; import com.protagonist.responseVO.StatusCode; import com.protagonist.servicebase.exception.ProtagonistException; import lombok.extern.slf4j.Slf4j; import java.util.Objects; @Slf4j public class SubjectExcelListener extends AnalysisEventListener<ExcelSubjectData> { //這裏必須來個構造器,將service傳過來,而後再監聽器中每處理一行就能夠直接進行入庫操做; //其實可使用mybatis的批量入庫的 private SubjectService subjectService; public SubjectExcelListener(SubjectService subjectService){ this.subjectService = subjectService; } //這個方法讀取excel內容,一行一行讀取,每一行都會觸發這個方法,而excelSubjectData表示的就是excel中的一行數據 //注意這裏的第一行不會讀,默認是表頭 @Override public void invoke(ExcelSubjectData excelSubjectData, AnalysisContext analysisContext) { log.info("每一行處理excel數據============================="); if (Objects.isNull(excelSubjectData)){ throw new ProtagonistException(StatusCode.ERROR,"文件內容爲空"); } //獲取每一行中的第一個數據和第二個數據,而後判斷數據庫中是否有該分類信息,沒有的話纔會插入數據庫 String oneSubjectName = excelSubjectData.getOneSubjectName(); String twoSubjectName = excelSubjectData.getTwoSubjectName(); //判斷數據庫中有沒有一級分類,沒有的話就插入一級分類 Subject oneSubject = getOneOrTwoSubject(subjectService, oneSubjectName, "0"); if (Objects.isNull(oneSubject)){ oneSubject = new Subject(); oneSubject.setTitle(oneSubjectName); oneSubject.setParentId("0"); subjectService.save(oneSubject); } log.info("數據入庫以後:{}", JSON.toJSONString(oneSubject)); //判斷數據庫中有沒有二級分類,要判斷這個二級分類的話必須先獲取此時的父id,這裏有個很神奇的地方,mybatisplus在執行save(T obj)方法完成以後, // 就會將生成的主鍵賦值給obj中 //oneSubject.getId()能夠獲取當前二級分類對應的一級分類的id Subject twoSubject = getOneOrTwoSubject(subjectService, twoSubjectName, oneSubject.getId()); if (Objects.isNull(twoSubject)){ twoSubject = new Subject(); twoSubject.setParentId(oneSubject.getId()); twoSubject.setTitle(twoSubjectName); subjectService.save(twoSubject); } } /** * 根據課程名稱和父id查詢課程分類表中的數據 * @param subjectService * @param name * @param pid * @return */ private Subject getOneOrTwoSubject(SubjectService subjectService,String name,String pid){ QueryWrapper<Subject> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("title",name); queryWrapper.eq("parent_id",pid); return subjectService.getOne(queryWrapper); } @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } }
8.測試成功