一.Excel導入導出的應用場景
1.數據導入:減輕錄入的工作量
2.數據導出:統計信息歸檔
3.數據傳輸:異構系統之間數據傳輸
二。EasyExcel簡介
1.EasyExcel特點
Java領域解析,生成Excel比較有名的框架有Apache poi,jxl等,但他們都存在一個嚴重的問題就是非常的耗內存,如果你的系統並發量不大的話可能還行,但是一旦並發上來后一定會OOM或者JVM頻繁的full gc.
EasyExcel是阿里巴巴開源的一個excel處理框架,以使用簡單,節省內存著稱,EasyExcel能大大減少占用內存的主要原因是在解析Excel時沒有將文件數據一次性全部加載到內存中,而是從磁盤上一行行讀取數據,逐個解析。
EasyExcel采用一行一行的解析模式,並將一行的解析結果以觀察者的模式通知處理(AnalysisEventListener)。
1.導入maven依賴,主要還需要poi的依賴,並且版本要對應上
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
2.編寫entity對象類
@Data public class DemoData { //設置excel表頭名稱 @ExcelProperty(value = "學生編號",index = 0) private Integer sno; @ExcelProperty(value = "學生姓名",index = 1) private String sname; }
3.寫操作
public class TestEasyExcelWrite { public static void main(String[] args) { //實現excel寫的操作 //1 設置寫入文件夾地址和excel文件名稱 String filename = "C:\\Users\\aus\\write.xlsx"; // 2 調用easyexcel里面的方法實現寫操作 // write方法兩個參數:第一個參數文件路徑名稱,第二個參數實體類class EasyExcel.write(filename,DemoData.class).sheet("學生列表").doWrite(getData()); } //創建方法返回list集合 private static List<DemoData> getData() { List<DemoData> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { DemoData data = new DemoData(); data.setSno(i); data.setSname("lucy"+i); list.add(data); } return list; } }
4.最終效果
5.讀取excel需要先寫一個監聽器繼承AnalysisEventListener
public class ExcelListener extends AnalysisEventListener<DemoData> { //一行一行讀取excel內容 @Override public void invoke(DemoData data, AnalysisContext analysisContext) { System.out.println("****"+data); } //讀取表頭內容 @Override public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { System.out.println("表頭:"+headMap); } //讀取完成之后 @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } }
6.讀取excel代碼
public class TestEasyExcelRead { public static void main(String[] args) { //實現excel讀操作 String filename = "C:\\Users\\aus\\write.xlsx"; EasyExcel.read(filename,DemoData.class,new ExcelListener()).sheet().doRead(); } }
打印效果見下圖:
實際項目中開發
entity
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @ApiModel(value="EduSubject對象", description="課程科目") public class EduSubject 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; @ApiModelProperty(value = "排序字段") private Integer sort; @ApiModelProperty(value = "創建時間") @TableField(fill = FieldFill.INSERT) private Date gmtCreate; @ApiModelProperty(value = "更新時間") @TableField(fill = FieldFill.INSERT) private Date gmtModified; }
@Data public class SubjectData { @ExcelProperty(index = 0) private String oneSubjectName; @ExcelProperty(index = 1) private String twoSubjectName; }
Controller層
@RestController @RequestMapping("/eduservice/subject") @CrossOrigin public class EduSubjectController { @Autowired private EduSubjectService subjectService; //添加課程分類 //獲取上傳過來文件,把文件內容讀取出來 @PostMapping("addSubject") public R addSubject(MultipartFile file) { //上傳過來excel文件 subjectService.saveSubject(file,subjectService); return R.ok(); } }
service層
@Service public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService { @Override public void saveSubject(MultipartFile file, EduSubjectService subjectService) { try{ InputStream in = file.getInputStream(); EasyExcel.read(in, SubjectData.class,new SubjectExcelListener(subjectService)).sheet().doRead(); } catch (Exception e) { e.printStackTrace(); } } }
5.監聽器繼承AnalysisEventListener,這里需要注意的點,因為業務層的SubjectExcelListener 並沒有交給Spring容器來管理,所以監聽器代碼無法注入EduSubjectService ,
這里通過構造方法的方式傳過來,從而完成對數據庫的操作。
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> { //因為SubjectExcelListener不交給spring進行管理,需要自己new,不能注入其他對象 //不能實現數據庫操作 public EduSubjectService subjectService; public SubjectExcelListener() {} public SubjectExcelListener(EduSubjectService subjectService) { this.subjectService = subjectService; } //讀取excel內容,一行一行進行讀取 @Override public void invoke(SubjectData subjectData, AnalysisContext analysisContext) { if(subjectData == null) { throw new GuliException(20001,"文件數據為空"); } //一行一行讀取,每次讀取有兩個值,第一個值一級分類,第二個值二級分類 //判斷一級分類是否重復 EduSubject existOneSubject = this.existOneSubject(subjectService, subjectData.getOneSubjectName()); if(existOneSubject == null) { //沒有相同一級分類,進行添加 existOneSubject = new EduSubject(); existOneSubject.setParentId("0"); existOneSubject.setTitle(subjectData.getOneSubjectName());//一級分類名稱 subjectService.save(existOneSubject); } //獲取一級分類id值 String pid = existOneSubject.getId(); //添加二級分類 //判斷二級分類是否重復 EduSubject existTwoSubject = this.existTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid); if(existTwoSubject == null) { existTwoSubject = new EduSubject(); existTwoSubject.setParentId(pid); existTwoSubject.setTitle(subjectData.getTwoSubjectName());//二級分類名稱 subjectService.save(existTwoSubject); } } //判斷一級分類不能重復添加 private EduSubject existOneSubject(EduSubjectService subjectService,String name) { QueryWrapper<EduSubject> wrapper = new QueryWrapper<>(); wrapper.eq("title",name); wrapper.eq("parent_id","0"); EduSubject oneSubject = subjectService.getOne(wrapper); return oneSubject; } //判斷二級分類不能重復添加 private EduSubject existTwoSubject(EduSubjectService subjectService,String name,String pid) { QueryWrapper<EduSubject> wrapper = new QueryWrapper<>(); wrapper.eq("title",name); wrapper.eq("parent_id",pid); EduSubject twoSubject = subjectService.getOne(wrapper); return twoSubject; } @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } }