easyexcel_easyexcel 下拉框 - CSDN
精华内容
参与话题
  • EasyExcel之导入导出

    千次阅读 2019-10-15 15:00:49
    JAVA解析Excel工具easyexcel Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些...

    JAVA解析Excel工具easyexcel

    Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便。

    github地址:https://github.com/alibaba/easyexcel

    依赖jar包

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.0.2</version>
    </dependency>

    读取Excel

    从2.0.x开始类似如下方法已是过时方法,可以使用推荐的方法去实现。

     

    1.无模板

    @Controller
    public class EasyExcelController {
    	
    	@RequestMapping("/readNoEntity")
    	@ResponseBody
    	public Map<String,Object> noEntity(MultipartFile excelFile) throws IOException {
    		Map<String,Object> result = new HashMap<>();
    		List<Object> list = EasyExcel.read(excelFile.getInputStream()).sheet(0).doReadSync();
    		result.put("list", list);
    		return result;	
    	}
    }

    2.有模板

    @Controller
    public class EasyExcelController {
    
    	@RequestMapping("/readToEntity")
    	@ResponseBody
    	public Map<String,Object> toEntity(MultipartFile excelFile) throws IOException {
    		Map<String,Object> result = new HashMap<>();
    		List<Object> list = EasyExcel.read(excelFile.getInputStream(),User.class,null).sheet(0).doReadSync();
    		result.put("list", list);
    		return result;	
    	}
    }

     模板Model同样从2.0.x开始就不需要再继承BaseRowModel了

    @Data
    public class User {
    	
    	private String name;
    	
    	private String account;
    	
    	private String address;
    }

     3.也可以根据需要自定义监听,只需要继承AnalysisEventListener

    import java.util.ArrayList;
    import java.util.List;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.event.AnalysisEventListener;
    import com.ruihao.zzx.client.demo.entity.User;
    
    public class ExcelListener extends AnalysisEventListener<User> {
    	
    	private final Logger LOGGER = LoggerFactory.getLogger(ExcelListener.class);
    	
    	private List<User> datas = new ArrayList<>();
    	//会读取每一行的数据
    	@Override
    	public void invoke(User object, AnalysisContext context) {
    		System.out.println(object);
    		datas.add(object);
    	}
    
    	@Override
    	public void doAfterAllAnalysed(AnalysisContext context) {
    		LOGGER.info("{}条数据,开始存储数据库!", datas.size());
    		LOGGER.info("所有数据解析完成!");
    	}
    
    	public List<User> getDatas() {
    		return datas;
    	}
    
    	public void setDatas(List<User> datas) {
    		this.datas = datas;
    	}
    
    }

     invoke方法会一行一行的读取excel表格中的数据,使用自定义监听如下

    @Controller
    public class EasyExcelController {
    	
    	@RequestMapping("/readToEntity")
    	@ResponseBody
    	public Map<String,Object> toEntity(MultipartFile excelFile) throws IOException {
    		Map<String,Object> result = new HashMap<>();
    		ExcelListener listener = new ExcelListener();
    		EasyExcel.read(excelFile.getInputStream(),User.class,listener).sheet(0).doRead();
    		result.put("list", listener.getDatas());
    		return result;	
    	}
    	
    }

    这里使用的是ExcelReaderSheetBuilder类的doRead()方法。doRead()和doReadSync()区别在于doReadSync()里面配了一个自定义的监听,并且返回读取到excel数据的List集合

        /**
         * Sax read
         */
        public void doRead() {
            if (excelReader == null) {
                throw new ExcelGenerateException("Must use 'EasyExcelFactory.read().sheet()' to call this method");
            }
            excelReader.read(build());
            excelReader.finish();
        }
    
        /**
         * Synchronous reads return results
         *
         * @return
         */
        public List<Object> doReadSync() {
            if (excelReader == null) {
                throw new ExcelAnalysisException("Must use 'EasyExcelFactory.read().sheet()' to call this method");
            }
            SyncReadListener syncReadListener = new SyncReadListener();
            registerReadListener(syncReadListener);
            excelReader.read(build());
            excelReader.finish();
            return syncReadListener.getList();
        }

     4.多个sheet的读取方法

    @Controller
    public class EasyExcelController {
    	
    	private final Logger LOGGER = LoggerFactory.getLogger(EasyExcelController.class);
    	
    	@RequestMapping("/readToEntity")
    	@ResponseBody
    	public Map<String,Object> toEntity(MultipartFile excelFile) throws IOException {
    		Map<String,Object> result = new HashMap<>();
    		ExcelListener listener = new ExcelListener();
    		ExcelReaderBuilder builder = EasyExcel.read(excelFile.getInputStream(),User.class,listener);
    		ExcelReader reader = builder.build();
    		//sheet集合
    		List<ReadSheet> sheets = reader.excelExecutor().sheetList();
    		for(ReadSheet sheet:sheets) {
    			listener.getDatas().clear();
    			LOGGER.info("sheet name:{}",sheet.getSheetName());
    			//读取每一个sheet的内容
    			reader.read(sheet);
    			List<User> current = listener.getDatas();
    			LOGGER.info("content:{}",JSONObject.toJSONString(current));
    		}
    		reader.finish();
    		result.put("list", listener.getDatas());
    		return result;	
    	}
    	
    }

     

    导出Excel

    1.无模板

    import java.net.URLEncoder;
    import java.util.ArrayList;
    import java.util.List;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import com.alibaba.excel.EasyExcel;
    
    @Controller
    public class DownloadController {
    	
    	@RequestMapping("/download")
    	public void download(HttpServletResponse response) throws Exception {
    		response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("测试", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");
            EasyExcel.write(response.getOutputStream()).sheet("第一个sheet").doWrite(data());;
    	}
    	
    	protected List<?> data(){
    		List<List<String>> rowList = new ArrayList<>();
    		List<String> cell = new ArrayList<>();
    		cell.add("good1");
    		cell.add("good2");
    		cell.add("good3");
    		rowList.add(cell);
    		return rowList;
    	}
    }

    2.有模板

    模板实体类

    import com.alibaba.excel.annotation.ExcelProperty;
    import lombok.Data;
    
    @Data
    public class Cell {
    	@ExcelProperty(value= {"主标题","第一列"})
    	private String cell1;
    	@ExcelProperty(value= {"主标题","第二列"})
    	private String cell2;
    	@ExcelProperty(value="第三列")
    	private String cell3;
    	@ExcelProperty(value="第四列")
    	private String cell4;
    	@ExcelProperty(value="第五列")
    	private String cell5;
    	@ExcelProperty(value="第六列")
    	private String cell6;
    	@ExcelProperty(value="第七列")
    	private String cell7;
    	@ExcelProperty(value="第八列")
    	private String cell8;
    	@ExcelProperty(value="第九列")
    	private String cell9;
    	@ExcelProperty(value="第十列")
    	private String cell10;
    }

    导出方法

    import java.net.URLEncoder;
    import java.util.ArrayList;
    import java.util.List;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import com.alibaba.excel.EasyExcel;
    
    @Controller
    public class DownloadController {
    	
    	@RequestMapping("/download")
    	public void download(HttpServletResponse response) throws Exception {
    		response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("测试", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");
            EasyExcel.write(response.getOutputStream(),Cell.class).sheet("第一个sheet").doWrite(data());;
    	}
    	
    	protected List<?> data(){
    		List<Cell> rowList = new ArrayList<>();
    		Cell cell = new Cell();
    		cell.setCell1("大师傅1");
    		cell.setCell2("大师傅2");
    		cell.setCell3("大师傅3");
    		cell.setCell4("大师傅4");
    		cell.setCell5("大师傅5");
    		cell.setCell6("大师傅6");
    		cell.setCell7("大师傅7");
    		cell.setCell8("大师傅8");
    		cell.setCell9("大师傅9");
    		cell.setCell10("大师傅20");
    		rowList.add(cell);
    		return rowList;
    	}
    }

    3.多个sheet导出

    @Controller
    public class DownloadController {
    	
    	@RequestMapping("/download")
    	public void download(HttpServletResponse response) throws Exception {
    		response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("测试", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");
            
            ExcelWriterBuilder builder =  EasyExcel.write(response.getOutputStream(),Cell.class);
            //注册单元格拦截器
            builder.registerWriteHandler(new CustomCellWriteHandler());
            //注册sheet拦截器
            builder.registerWriteHandler(new CustomSheetWriteHandler());
            ExcelWriter excelWriter = builder.build();
            
            WriteSheet writeSheet =null;
            writeSheet = EasyExcel.writerSheet(0, "TSBD").build();
            excelWriter.write(data(), writeSheet);
            writeSheet = EasyExcel.writerSheet(1, "BSBD").build();
            excelWriter.write(data2(), writeSheet);
            excelWriter.finish();
    	}
    	
    	protected List<?> data(){
    		List<Cell> rowList = new ArrayList<>();
    		Cell cell = new Cell();
    		cell.setCell1("大师傅1");
    		cell.setCell2("大师傅2");
    		cell.setCell3("大师傅3");
    		cell.setCell4("大师傅4");
    		cell.setCell5("大师傅5");
    		cell.setCell6("大师傅6");
    		cell.setCell7("大师傅7");
    		cell.setCell8("大师傅8");
    		cell.setCell9("大师傅9");
    		cell.setCell10("大师傅20");
    		rowList.add(cell);
    		return rowList;
    	}
    	
    	protected List<?> data2(){
    		List<Cell> rowList = new ArrayList<>();
    		Cell cell = new Cell();
    		cell.setCell1("大师傅11");
    		cell.setCell2("大师傅21");
    		cell.setCell3("大师傅31");
    		cell.setCell4("大师傅41");
    		cell.setCell5("大师傅51");
    		cell.setCell6("大师傅61");
    		cell.setCell7("大师傅71");
    		cell.setCell8("大师傅81");
    		cell.setCell9("大师傅91");
    		cell.setCell10("大师傅10");
    		rowList.add(cell);
    		return rowList;
    	}

    4.配置单元格和sheet拦截器

    单元格拦截器

    import org.apache.poi.common.usermodel.HyperlinkType;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.CreationHelper;
    import org.apache.poi.ss.usermodel.Hyperlink;
    import org.apache.poi.ss.usermodel.Row;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.alibaba.excel.metadata.CellData;
    import com.alibaba.excel.metadata.Head;
    import com.alibaba.excel.write.handler.CellWriteHandler;
    import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
    import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
    /**
     * 自定义单元格拦截器
     * @author 
     * 2019年10月12日
     */
    public class CustomCellWriteHandler implements CellWriteHandler {
    	
    	private final Logger LOGGER = LoggerFactory.getLogger(CustomCellWriteHandler.class);
    	
    	@Override
    	public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
    			Head head, int relativeRowIndex, boolean isHead) {
    	}
    
    	@Override
    	public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData,
    			Cell cell, Head head, int relativeRowIndex, boolean isHead) {
    		// 这里可以对cell进行任何操作
            LOGGER.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex());
            if (isHead && cell.getColumnIndex() == 0) {
                CreationHelper createHelper = writeSheetHolder.getSheet().getWorkbook().getCreationHelper();
                Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL);
                hyperlink.setAddress("https://github.com/alibaba/easyexcel");
                cell.setHyperlink(hyperlink);
            }
    	}
    
    }

    sheet拦截器

    import org.apache.poi.ss.usermodel.DataValidation;
    import org.apache.poi.ss.usermodel.DataValidationConstraint;
    import org.apache.poi.ss.usermodel.DataValidationHelper;
    import org.apache.poi.ss.util.CellRangeAddressList;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.alibaba.excel.write.handler.SheetWriteHandler;
    import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
    import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
    
    /**
     * 自定义sheet拦截器
     * @author
     * 2019年10月12日
     */
    public class CustomSheetWriteHandler implements SheetWriteHandler {
    	
    	private final Logger LOGGER = LoggerFactory.getLogger(CustomSheetWriteHandler.class);
    	
    	@Override
    	public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    	}
    
    	@Override
    	public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    		LOGGER.info("第{}个Sheet写入成功。", writeSheetHolder.getSheetNo());
    
            // 区间设置 第三行第一列和第二列的数据。
            CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(2, 2, 0, 1);
            DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();
            DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"康师傅", "汤达人"});
            DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
            writeSheetHolder.getSheet().addValidationData(dataValidation);
    	}
    
    }

    配置

        @RequestMapping("/download")
    	public void download(HttpServletResponse response) throws Exception {
    		response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("测试", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");
            
            ExcelWriterBuilder builder =  EasyExcel.write(response.getOutputStream(),Cell.class);
            //注册单元格拦截器
            builder.registerWriteHandler(new CustomCellWriteHandler());
            //注册sheet拦截器
            builder.registerWriteHandler(new CustomSheetWriteHandler());
            ExcelWriter excelWriter = builder.build();
            
            WriteSheet writeSheet =null;
            writeSheet = EasyExcel.writerSheet(0, "TSBD").build();
            excelWriter.write(data(), writeSheet);
            writeSheet = EasyExcel.writerSheet(1, "BSBD").build();
            excelWriter.write(data2(), writeSheet);
            excelWriter.finish();
    	}

    附:另一款Excel解析工具EasyPOI也挺好用的,复杂模板导出

    git地址:https://gitee.com/lemur/easypoi

    开源文档:https://opensource.afterturn.cn/doc/easypoi.html

    展开全文
  • 史上最全的Excel导入导出之easyexcel

    万次阅读 多人点赞 2019-04-08 11:58:28
    文章目录环境搭建读取excel文件小于1000行数据默认读取指定读取大于1000行数据...easyexcel 依赖(必须) springboot (不是必须) lombok (不是必须) <dependency> <groupId>com.alibaba</...

    喝水不忘挖井人,感谢阿里巴巴项目组提供了easyexcel工具类,github地址:https://github.com/alibaba/easyexcel

    环境搭建

    • easyexcel 依赖(必须)
    • springboot (不是必须)
    • lombok (不是必须)
     <dependency>
    	  <groupId>com.alibaba</groupId>
    	  <artifactId>easyexcel</artifactId>
    	  <version>1.1.2-beat1</version>
     </dependency>
      
     <dependency>
    	  <groupId>org.projectlombok</groupId>
    	  <artifactId>lombok</artifactId>
    	  <version>1.18.2</version>
     </dependency>
    

    读取excel文件

    小于1000行数据

    默认读取

    读取Sheet1的全部数据

     String filePath = "/home/chenmingjian/Downloads/学生表.xlsx";
     List<Object> objects = ExcelUtil.readLessThan1000Row(filePath);
    

    指定读取

    下面是学生表.xlsx中Sheet1,Sheet2的数据
    在这里插入图片描述
    在这里插入图片描述
    获取Sheet1表头以下的信息

    String filePath = "/home/chenmingjian/Downloads/学生表.xlsx";
    //第一个1代表sheet1, 第二个1代表从第几行开始读取数据,行号最小值为0
    Sheet sheet = new Sheet(1, 1);
    List<Object> objects = ExcelUtil.readLessThan1000Row(filePath,sheet);
    

    获取Sheet2的所有信息

     String filePath = "/home/chenmingjian/Downloads/学生表.xlsx";
     Sheet sheet = new Sheet(2, 0);
     List<Object> objects = ExcelUtil.readLessThan1000Row(filePath,sheet);
    

    大于1000行数据

    默认读取

    String filePath = "/home/chenmingjian/Downloads/学生表.xlsx";
    List<Object> objects = ExcelUtil.readMoreThan1000Row(filePath);
    

    指定读取

    String filePath = "/home/chenmingjian/Downloads/学生表.xlsx";
    Sheet sheet = new Sheet(1, 2);
    List<Object> objects = ExcelUtil.readMoreThan1000Row(filePath,sheet);
    

    导出excle

    单个Sheet导出

    无模型映射导出

    String filePath = "/home/chenmingjian/Downloads/测试.xlsx";
    List<List<Object>> data = new ArrayList<>();
    data.add(Arrays.asList("111","222","333"));
    data.add(Arrays.asList("111","222","333"));
    data.add(Arrays.asList("111","222","333"));
    List<String> head = Arrays.asList("表头1", "表头2", "表头3");
    ExcelUtil.writeBySimple(filePath,data,head);
    

    结果
    在这里插入图片描述

    模型映射导出

    1、定义好模型对象

    package com.springboot.utils.excel.test;
    
    import com.alibaba.excel.annotation.ExcelProperty;
    import com.alibaba.excel.metadata.BaseRowModel;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    
    /**
     * @description:
     * @author: chenmingjian
     * @date: 19-4-3 14:44
     */
    @EqualsAndHashCode(callSuper = true)
    @Data
    public class TableHeaderExcelProperty extends BaseRowModel {
    
        /**
         * value: 表头名称
         * index: 列的号, 0表示第一列
         */
        @ExcelProperty(value = "姓名", index = 0)
        private String name;
    
        @ExcelProperty(value = "年龄",index = 1)
        private int age;
    
        @ExcelProperty(value = "学校",index = 2)
        private String school;
    }
    
    

    2、调用方法

    String filePath = "/home/chenmingjian/Downloads/测试.xlsx";
    ArrayList<TableHeaderExcelProperty> data = new ArrayList<>();
      for(int i = 0; i < 4; i++){
          TableHeaderExcelProperty tableHeaderExcelProperty = new TableHeaderExcelProperty();
          tableHeaderExcelProperty.setName("cmj" + i);
          tableHeaderExcelProperty.setAge(22 + i);
          tableHeaderExcelProperty.setSchool("清华大学" + i);
          data.add(tableHeaderExcelProperty);
      }
    
      ExcelUtil.writeWithTemplate(filePath,data);
    

    多个Sheet导出

    1、定义好模型对象

    package com.springboot.utils.excel.test;
    
    import com.alibaba.excel.annotation.ExcelProperty;
    import com.alibaba.excel.metadata.BaseRowModel;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    
    /**
     * @description:
     * @author: chenmingjian
     * @date: 19-4-3 14:44
     */
    @EqualsAndHashCode(callSuper = true)
    @Data
    public class TableHeaderExcelProperty extends BaseRowModel {
    
        /**
         * value: 表头名称
         * index: 列的号, 0表示第一列
         */
        @ExcelProperty(value = "姓名", index = 0)
        private String name;
    
        @ExcelProperty(value = "年龄",index = 1)
        private int age;
    
        @ExcelProperty(value = "学校",index = 2)
        private String school;
    }
    
    

    2、调用方法

     ArrayList<ExcelUtil.MultipleSheelPropety> list1 = new ArrayList<>();
     for(int j = 1; j < 4; j++){
          ArrayList<TableHeaderExcelProperty> list = new ArrayList<>();
          for(int i = 0; i < 4; i++){
              TableHeaderExcelProperty tableHeaderExcelProperty = new TableHeaderExcelProperty();
              tableHeaderExcelProperty.setName("cmj" + i);
              tableHeaderExcelProperty.setAge(22 + i);
              tableHeaderExcelProperty.setSchool("清华大学" + i);
              list.add(tableHeaderExcelProperty);
          }
    
          Sheet sheet = new Sheet(j, 0);
          sheet.setSheetName("sheet" + j);
    
          ExcelUtil.MultipleSheelPropety multipleSheelPropety = new ExcelUtil.MultipleSheelPropety();
          multipleSheelPropety.setData(list);
          multipleSheelPropety.setSheet(sheet);
    
          list1.add(multipleSheelPropety);
    
      }
    
      ExcelUtil.writeWithMultipleSheel("/home/chenmingjian/Downloads/aaa.xlsx",list1);
    

    工具类

    package com.springboot.utils.excel;
    
    import com.alibaba.excel.EasyExcelFactory;
    import com.alibaba.excel.ExcelWriter;
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.event.AnalysisEventListener;
    import com.alibaba.excel.metadata.BaseRowModel;
    import com.alibaba.excel.metadata.Sheet;
    import lombok.Data;
    import lombok.Getter;
    import lombok.Setter;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.util.CollectionUtils;
    import org.springframework.util.StringUtils;
    
    import java.io.*;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * @description:
     * @author: chenmingjian
     * @date: 19-3-18 16:16
     */
    @Slf4j
    public class ExcelUtil {
    
       private static Sheet initSheet;
    
       static {
          initSheet = new Sheet(1, 0);
          initSheet.setSheetName("sheet");
          //设置自适应宽度
          initSheet.setAutoWidth(Boolean.TRUE);
       }
    
       /**
        * 读取少于1000行数据
        * @param filePath 文件绝对路径
        * @return
        */
       public static List<Object> readLessThan1000Row(String filePath){
          return readLessThan1000RowBySheet(filePath,null);
       }
    
       /**
        * 读小于1000行数据, 带样式
        * filePath 文件绝对路径
        * initSheet :
        *      sheetNo: sheet页码,默认为1
        *      headLineMun: 从第几行开始读取数据,默认为0, 表示从第一行开始读取
        *      clazz: 返回数据List<Object> 中Object的类名
        */
       public static List<Object> readLessThan1000RowBySheet(String filePath, Sheet sheet){
          if(!StringUtils.hasText(filePath)){
             return null;
          }
    
          sheet = sheet != null ? sheet : initSheet;
    
          InputStream fileStream = null;
          try {
             fileStream = new FileInputStream(filePath);
             return EasyExcelFactory.read(fileStream, sheet);
          } catch (FileNotFoundException e) {
             log.info("找不到文件或文件路径错误, 文件:{}", filePath);
          }finally {
             try {
                if(fileStream != null){
                   fileStream.close();
                }
             } catch (IOException e) {
                log.info("excel文件读取失败, 失败原因:{}", e);
             }
          }
          return null;
       }
    
       /**
        * 读大于1000行数据
        * @param filePath 文件觉得路径
        * @return
        */
       public static List<Object> readMoreThan1000Row(String filePath){
          return readMoreThan1000RowBySheet(filePath,null);
       }
    
       /**
        * 读大于1000行数据, 带样式
        * @param filePath 文件觉得路径
        * @return
        */
       public static List<Object> readMoreThan1000RowBySheet(String filePath, Sheet sheet){
          if(!StringUtils.hasText(filePath)){
             return null;
          }
    
          sheet = sheet != null ? sheet : initSheet;
    
          InputStream fileStream = null;
          try {
             fileStream = new FileInputStream(filePath);
             ExcelListener excelListener = new ExcelListener();
             EasyExcelFactory.readBySax(fileStream, sheet, excelListener);
             return excelListener.getDatas();
          } catch (FileNotFoundException e) {
             log.error("找不到文件或文件路径错误, 文件:{}", filePath);
          }finally {
             try {
                if(fileStream != null){
                   fileStream.close();
                }
             } catch (IOException e) {
                log.error("excel文件读取失败, 失败原因:{}", e);
             }
          }
          return null;
       }
    
       /**
        * 生成excle
        * @param filePath  绝对路径, 如:/home/chenmingjian/Downloads/aaa.xlsx
        * @param data 数据源
        * @param head 表头
        */
       public static void writeBySimple(String filePath, List<List<Object>> data, List<String> head){
          writeSimpleBySheet(filePath,data,head,null);
       }
    
       /**
        * 生成excle
        * @param filePath 绝对路径, 如:/home/chenmingjian/Downloads/aaa.xlsx
        * @param data 数据源
        * @param sheet excle页面样式
        * @param head 表头
        */
       public static void writeSimpleBySheet(String filePath, List<List<Object>> data, List<String> head, Sheet sheet){
          sheet = (sheet != null) ? sheet : initSheet;
    
          if(head != null){
             List<List<String>> list = new ArrayList<>();
             head.forEach(h -> list.add(Collections.singletonList(h)));
             sheet.setHead(list);
          }
    
          OutputStream outputStream = null;
          ExcelWriter writer = null;
          try {
             outputStream = new FileOutputStream(filePath);
             writer = EasyExcelFactory.getWriter(outputStream);
             writer.write1(data,sheet);
          } catch (FileNotFoundException e) {
             log.error("找不到文件或文件路径错误, 文件:{}", filePath);
          }finally {
             try {
                if(writer != null){
                   writer.finish();
                }
    
                if(outputStream != null){
                   outputStream.close();
                }
    
             } catch (IOException e) {
                log.error("excel文件导出失败, 失败原因:{}", e);
             }
          }
    
       }
    
       /**
        * 生成excle
        * @param filePath 绝对路径, 如:/home/chenmingjian/Downloads/aaa.xlsx
        * @param data 数据源
        */
       public static void writeWithTemplate(String filePath, List<? extends BaseRowModel> data){
          writeWithTemplateAndSheet(filePath,data,null);
       }
    
       /**
        * 生成excle
        * @param filePath 绝对路径, 如:/home/chenmingjian/Downloads/aaa.xlsx
        * @param data 数据源
        * @param sheet excle页面样式
        */
       public static void writeWithTemplateAndSheet(String filePath, List<? extends BaseRowModel> data, Sheet sheet){
          if(CollectionUtils.isEmpty(data)){
             return;
          }
    
          sheet = (sheet != null) ? sheet : initSheet;
          sheet.setClazz(data.get(0).getClass());
    
          OutputStream outputStream = null;
          ExcelWriter writer = null;
          try {
             outputStream = new FileOutputStream(filePath);
             writer = EasyExcelFactory.getWriter(outputStream);
             writer.write(data,sheet);
          } catch (FileNotFoundException e) {
             log.error("找不到文件或文件路径错误, 文件:{}", filePath);
          }finally {
             try {
                if(writer != null){
                   writer.finish();
                }
    
                if(outputStream != null){
                   outputStream.close();
                }
             } catch (IOException e) {
                log.error("excel文件导出失败, 失败原因:{}", e);
             }
          }
    
       }
    
       /**
        * 生成多Sheet的excle
        * @param filePath 绝对路径, 如:/home/chenmingjian/Downloads/aaa.xlsx
        * @param multipleSheelPropetys
        */
       public static void writeWithMultipleSheel(String filePath,List<MultipleSheelPropety> multipleSheelPropetys){
          if(CollectionUtils.isEmpty(multipleSheelPropetys)){
             return;
          }
    
          OutputStream outputStream = null;
          ExcelWriter writer = null;
          try {
             outputStream = new FileOutputStream(filePath);
             writer = EasyExcelFactory.getWriter(outputStream);
             for (MultipleSheelPropety multipleSheelPropety : multipleSheelPropetys) {
                Sheet sheet = multipleSheelPropety.getSheet() != null ? multipleSheelPropety.getSheet() : initSheet;
                if(!CollectionUtils.isEmpty(multipleSheelPropety.getData())){
                   sheet.setClazz(multipleSheelPropety.getData().get(0).getClass());
                }
                writer.write(multipleSheelPropety.getData(), sheet);
             }
    
          } catch (FileNotFoundException e) {
             log.error("找不到文件或文件路径错误, 文件:{}", filePath);
          }finally {
             try {
                if(writer != null){
                   writer.finish();
                }
    
                if(outputStream != null){
                   outputStream.close();
                }
             } catch (IOException e) {
                log.error("excel文件导出失败, 失败原因:{}", e);
             }
          }
    
       }
    
    
       /*********************匿名内部类开始,可以提取出去******************************/
    
       @Data
       public static class MultipleSheelPropety{
    
          private List<? extends BaseRowModel> data;
    
          private Sheet sheet;
       }
    
       /**
        * 解析监听器,
        * 每解析一行会回调invoke()方法。
        * 整个excel解析结束会执行doAfterAllAnalysed()方法
        *
        * @author: chenmingjian
        * @date: 19-4-3 14:11
        */
       @Getter
       @Setter
       public static class ExcelListener extends AnalysisEventListener {
    
          private List<Object> datas = new ArrayList<>();
    
          /**
           * 逐行解析
           * object : 当前行的数据
           */
          @Override
          public void invoke(Object object, AnalysisContext context) {
             //当前行
             // context.getCurrentRowNum()
             if (object != null) {
                datas.add(object);
             }
          }
    
    
          /**
           * 解析完所有数据后会调用该方法
           */
          @Override
          public void doAfterAllAnalysed(AnalysisContext context) {
             //解析结束销毁不用的资源
          }
    
       }
    
       /************************匿名内部类结束,可以提取出去***************************/
    
    }
    
    

    测试类

    package com.springboot.utils.excel;
    
    import com.alibaba.excel.annotation.ExcelProperty;
    import com.alibaba.excel.metadata.BaseRowModel;
    import com.alibaba.excel.metadata.Sheet;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * @description: 测试类
     * @author: chenmingjian
     * @date: 19-4-4 15:24
     */
    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class Test {
    
        /**
         * 读取少于1000行的excle
         */
        @org.junit.Test
        public void readLessThan1000Row(){
            String filePath = "/home/chenmingjian/Downloads/测试.xlsx";
            List<Object> objects = ExcelUtil.readLessThan1000Row(filePath);
            objects.forEach(System.out::println);
        }
    
        /**
         * 读取少于1000行的excle,可以指定sheet和从几行读起
         */
        @org.junit.Test
        public void readLessThan1000RowBySheet(){
            String filePath = "/home/chenmingjian/Downloads/测试.xlsx";
            Sheet sheet = new Sheet(1, 1);
            List<Object> objects = ExcelUtil.readLessThan1000RowBySheet(filePath,sheet);
            objects.forEach(System.out::println);
        }
    
        /**
         * 读取大于1000行的excle
         * 带sheet参数的方法可参照测试方法readLessThan1000RowBySheet()
         */
        @org.junit.Test
        public void readMoreThan1000Row(){
            String filePath = "/home/chenmingjian/Downloads/测试.xlsx";
            List<Object> objects = ExcelUtil.readMoreThan1000Row(filePath);
            objects.forEach(System.out::println);
        }
    
    
        /**
         * 生成excle
         * 带sheet参数的方法可参照测试方法readLessThan1000RowBySheet()
         */
        @org.junit.Test
        public void writeBySimple(){
            String filePath = "/home/chenmingjian/Downloads/测试.xlsx";
            List<List<Object>> data = new ArrayList<>();
            data.add(Arrays.asList("111","222","333"));
            data.add(Arrays.asList("111","222","333"));
            data.add(Arrays.asList("111","222","333"));
            List<String> head = Arrays.asList("表头1", "表头2", "表头3");
            ExcelUtil.writeBySimple(filePath,data,head);
        }
    
    
        /**
         * 生成excle, 带用模型
         * 带sheet参数的方法可参照测试方法readLessThan1000RowBySheet()
         */
        @org.junit.Test
        public void writeWithTemplate(){
            String filePath = "/home/chenmingjian/Downloads/测试.xlsx";
            ArrayList<TableHeaderExcelProperty> data = new ArrayList<>();
            for(int i = 0; i < 4; i++){
                TableHeaderExcelProperty tableHeaderExcelProperty = new TableHeaderExcelProperty();
                tableHeaderExcelProperty.setName("cmj" + i);
                tableHeaderExcelProperty.setAge(22 + i);
                tableHeaderExcelProperty.setSchool("清华大学" + i);
                data.add(tableHeaderExcelProperty);
            }
            ExcelUtil.writeWithTemplate(filePath,data);
        }
    
    
        /**
         * 生成excle, 带用模型,带多个sheet
         */
        @org.junit.Test
        public void writeWithMultipleSheel(){
            ArrayList<ExcelUtil.MultipleSheelPropety> list1 = new ArrayList<>();
            for(int j = 1; j < 4; j++){
                ArrayList<TableHeaderExcelProperty> list = new ArrayList<>();
                for(int i = 0; i < 4; i++){
                    TableHeaderExcelProperty tableHeaderExcelProperty = new TableHeaderExcelProperty();
                    tableHeaderExcelProperty.setName("cmj" + i);
                    tableHeaderExcelProperty.setAge(22 + i);
                    tableHeaderExcelProperty.setSchool("清华大学" + i);
                    list.add(tableHeaderExcelProperty);
                }
    
                Sheet sheet = new Sheet(j, 0);
                sheet.setSheetName("sheet" + j);
    
                ExcelUtil.MultipleSheelPropety multipleSheelPropety = new ExcelUtil.MultipleSheelPropety();
                multipleSheelPropety.setData(list);
                multipleSheelPropety.setSheet(sheet);
    
                list1.add(multipleSheelPropety);
    
            }
    
            ExcelUtil.writeWithMultipleSheel("/home/chenmingjian/Downloads/aaa.xlsx",list1);
    
        }
    
    
        /*******************匿名内部类,实际开发中该对象要提取出去**********************/
        
        
        /**
         * @description:
         * @author: chenmingjian
         * @date: 19-4-3 14:44
         */
        @EqualsAndHashCode(callSuper = true)
        @Data
        public static class TableHeaderExcelProperty extends BaseRowModel {
    
            /**
             * value: 表头名称
             * index: 列的号, 0表示第一列
             */
            @ExcelProperty(value = "姓名", index = 0)
            private String name;
    
            @ExcelProperty(value = "年龄",index = 1)
            private int age;
    
            @ExcelProperty(value = "学校",index = 2)
            private String school;
        }
    
        /*******************匿名内部类,实际开发中该对象要提取出去**********************/
    
    }
    
    
    
    展开全文
  • 阿里开源(EasyExcel)---导出EXCEL

    万次阅读 多人点赞 2019-11-27 09:37:31
    一. 简介 导出是后台管理系统的常用功能,当数据量特别大的时候会内存溢出和卡顿页面,曾经自己封装过一个导出,POI百万级大数据量EXCEL导出采用了分批查询数据来避免内存溢出和使用SXSSFWorkbook方式缓存数据到...

    一. 简介

             导出是后台管理系统的常用功能,当数据量特别大的时候会内存溢出和卡顿页面,曾经自己封装过一个导出,POI百万级大数据量EXCEL导出 采用了分批查询数据来避免内存溢出和使用SXSSFWorkbook方式缓存数据到文件上以解决下载大文件EXCEL卡死页面的问题。不过一是存在封装不太友好使用不方便的问题,二是这些poi的操作方式仍然存在内存占用过大的问题,三是存在空循环和整除的时候数据有缺陷的问题,以及存在内存溢出的隐患。无意间查询到阿里开源的EasyExcel框架,发现可以将解析的EXCEL的内存占用控制在KB级别,并且绝对不会内存溢出(内部实现待研究),还有就是速度极快, 大概100W条记录,十几个字段, 只需要70秒即可完成下载。遂抛弃自己封装的,转战研究阿里开源的EasyExcel. 不过 说实话,当时自己封装的那个还是有些技术含量的,例如 外观模式,模板方法模式,以及委托思想,组合思想,可以看看。

             EasyExcel的github地址是:https://github.com/alibaba/easyexcel 

    二. 案例

    2.1 POM依赖

            <!-- 阿里开源EXCEL -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>1.1.1</version>
            </dependency>

    2.2 POJO对象

    package com.authorization.privilege.excel;
    
    import java.util.Date;
    
    /**
     * @author qjwyss
     * @date 2019/3/15
     * @description
     */
    public class User {
    
        private String uid;
        private String name;
        private Integer age;
        private Date birthday;
    
        public User() {
        }
    
        public User(String uid, String name, Integer age, Date birthday) {
            this.uid = uid;
            this.name = name;
            this.age = age;
            this.birthday = birthday;
        }
    
        public String getUid() {
            return uid;
        }
    
        public void setUid(String uid) {
            this.uid = uid;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    }
    

     

    2.3 测试环境

    2.3.1 数据量少的(20W以内吧):一个SHEET一次查询导出

        /**
         * 针对较少的记录数(20W以内大概)可以调用该方法一次性查出然后写入到EXCEL的一个SHEET中
         * 注意: 一次性查询出来的记录数量不宜过大,不会内存溢出即可。
         *
         * @throws IOException
         */
        @Test
        public void writeExcelOneSheetOnceWrite() throws IOException {
    
            // 生成EXCEL并指定输出路径
            OutputStream out = new FileOutputStream("E:\\temp\\withoutHead1.xlsx");
            ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
    
            // 设置SHEET
            Sheet sheet = new Sheet(1, 0);
            sheet.setSheetName("sheet1");
    
            // 设置标题
            Table table = new Table(1);
            List<List<String>> titles = new ArrayList<List<String>>();
            titles.add(Arrays.asList("用户ID"));
            titles.add(Arrays.asList("名称"));
            titles.add(Arrays.asList("年龄"));
            titles.add(Arrays.asList("生日"));
            table.setHead(titles);
    
            // 查询数据导出即可 比如说一次性总共查询出100条数据
            List<List<String>> userList = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                userList.add(Arrays.asList("ID_" + i, "小明" + i, String.valueOf(i), new Date().toString()));
            }
    
            writer.write0(userList, sheet, table);
            writer.finish();
        }

     

    2.3.2 数据量适中(100W以内): 一个SHEET分批查询导出

        /**
         * 针对105W以内的记录数可以调用该方法分多批次查出然后写入到EXCEL的一个SHEET中
         * 注意:
         * 每次查询出来的记录数量不宜过大,根据内存大小设置合理的每次查询记录数,不会内存溢出即可。
         * 数据量不能超过一个SHEET存储的最大数据量105W
         *
         * @throws IOException
         */
        @Test
        public void writeExcelOneSheetMoreWrite() throws IOException {
    
            // 生成EXCEL并指定输出路径
            OutputStream out = new FileOutputStream("E:\\temp\\withoutHead2.xlsx");
            ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
    
            // 设置SHEET
            Sheet sheet = new Sheet(1, 0);
            sheet.setSheetName("sheet1");
    
            // 设置标题
            Table table = new Table(1);
            List<List<String>> titles = new ArrayList<List<String>>();
            titles.add(Arrays.asList("用户ID"));
            titles.add(Arrays.asList("名称"));
            titles.add(Arrays.asList("年龄"));
            titles.add(Arrays.asList("生日"));
            table.setHead(titles);
    
            // 模拟分批查询:总记录数50条,每次查询20条,  分三次查询 最后一次查询记录数是10
            Integer totalRowCount = 50;
            Integer pageSize = 20;
            Integer writeCount = totalRowCount % pageSize == 0 ? (totalRowCount / pageSize) : (totalRowCount / pageSize + 1);
    
            // 注: 此处仅仅为了模拟数据,实用环境不需要将最后一次分开,合成一个即可, 参数为: currentPage = i+1;  pageSize = pageSize
            for (int i = 0; i < writeCount; i++) {
    
                // 前两次查询 每次查20条数据
                if (i < writeCount - 1) {
    
                    List<List<String>> userList = new ArrayList<>();
                    for (int j = 0; j < pageSize; j++) {
                        userList.add(Arrays.asList("ID_" + Math.random(), "小明", String.valueOf(Math.random()), new Date().toString()));
                    }
                    writer.write0(userList, sheet, table);
    
                } else if (i == writeCount - 1) {
    
                    // 最后一次查询 查多余的10条记录
                    List<List<String>> userList = new ArrayList<>();
                    Integer lastWriteRowCount = totalRowCount - (writeCount - 1) * pageSize;
                    for (int j = 0; j < lastWriteRowCount; j++) {
                        userList.add(Arrays.asList("ID_" + Math.random(), "小明", String.valueOf(Math.random()), new Date().toString()));
                    }
                    writer.write0(userList, sheet, table);
                }
            }
    
            writer.finish();
        }

     

    2.3.3 数据量很大(几百万都行): 多个SHEET分批查询导出 

        /**
         * 针对几百万的记录数可以调用该方法分多批次查出然后写入到EXCEL的多个SHEET中
         * 注意:
         * perSheetRowCount % pageSize要能整除  为了简洁,非整除这块不做处理
         * 每次查询出来的记录数量不宜过大,根据内存大小设置合理的每次查询记录数,不会内存溢出即可。
         *
         * @throws IOException
         */
        @Test
        public void writeExcelMoreSheetMoreWrite() throws IOException {
    
            // 生成EXCEL并指定输出路径
            OutputStream out = new FileOutputStream("E:\\temp\\withoutHead3.xlsx");
            ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
    
            // 设置SHEET名称
            String sheetName = "测试SHEET";
    
            // 设置标题
            Table table = new Table(1);
            List<List<String>> titles = new ArrayList<List<String>>();
            titles.add(Arrays.asList("用户ID"));
            titles.add(Arrays.asList("名称"));
            titles.add(Arrays.asList("年龄"));
            titles.add(Arrays.asList("生日"));
            table.setHead(titles);
    
            // 模拟分批查询:总记录数250条,每个SHEET存100条,每次查询20条  则生成3个SHEET,前俩个SHEET查询次数为5, 最后一个SHEET查询次数为3 最后一次写的记录数是10
            // 注:该版本为了较少数据判断的复杂度,暂时perSheetRowCount要能够整除pageSize, 不去做过多处理  合理分配查询数据量大小不会内存溢出即可。
            Integer totalRowCount = 250;
            Integer perSheetRowCount = 100;
            Integer pageSize = 20;
            Integer sheetCount = totalRowCount % perSheetRowCount == 0 ? (totalRowCount / perSheetRowCount) : (totalRowCount / perSheetRowCount + 1);
            Integer previousSheetWriteCount = perSheetRowCount / pageSize;
            Integer lastSheetWriteCount = totalRowCount % perSheetRowCount == 0 ?
                    previousSheetWriteCount :
                    (totalRowCount % perSheetRowCount % pageSize == 0 ? totalRowCount % perSheetRowCount / pageSize : (totalRowCount % perSheetRowCount / pageSize + 1));
    
            for (int i = 0; i < sheetCount; i++) {
    
                // 创建SHEET
                Sheet sheet = new Sheet(i, 0);
                sheet.setSheetName(sheetName + i);
    
                if (i < sheetCount - 1) {
    
                    // 前2个SHEET, 每个SHEET查5次 每次查20条 每个SHEET写满100行  2个SHEET合计200行  实用环境:参数: currentPage: j+1 + previousSheetWriteCount*i, pageSize: pageSize
                    for (int j = 0; j < previousSheetWriteCount; j++) {
                        List<List<String>> userList = new ArrayList<>();
                        for (int k = 0; k < 20; k++) {
                            userList.add(Arrays.asList("ID_" + Math.random(), "小明", String.valueOf(Math.random()), new Date().toString()));
                        }
                        writer.write0(userList, sheet, table);
                    }
    
                } else if (i == sheetCount - 1) {
    
                    // 最后一个SHEET 实用环境不需要将最后一次分开,合成一个即可, 参数为: currentPage = i+1;  pageSize = pageSize
                    for (int j = 0; j < lastSheetWriteCount; j++) {
    
                        // 前俩次查询 每次查询20条
                        if (j < lastSheetWriteCount - 1) {
    
                            List<List<String>> userList = new ArrayList<>();
                            for (int k = 0; k < 20; k++) {
                                userList.add(Arrays.asList("ID_" + Math.random(), "小明", String.valueOf(Math.random()), new Date().toString()));
                            }
                            writer.write0(userList, sheet, table);
    
                        } else if (j == lastSheetWriteCount - 1) {
    
                            // 最后一次查询 将剩余的10条查询出来
                            List<List<String>> userList = new ArrayList<>();
                            Integer lastWriteRowCount = totalRowCount - (sheetCount - 1) * perSheetRowCount - (lastSheetWriteCount - 1) * pageSize;
                            for (int k = 0; k < lastWriteRowCount; k++) {
                                userList.add(Arrays.asList("ID_" + Math.random(), "小明1", String.valueOf(Math.random()), new Date().toString()));
                            }
                            writer.write0(userList, sheet, table);
    
                        }
                    }
                }
            }
    
            writer.finish();
        }

     

    2.4 生产环境

    2.4.0 Excel常量类

    package com.authorization.privilege.constant;
    
    /**
     * @author qjwyss
     * @date 2019/3/18
     * @description EXCEL常量类
     */
    public class ExcelConstant {
    
        /**
         * 每个sheet存储的记录数 100W
         */
        public static final Integer PER_SHEET_ROW_COUNT = 1000000;
    
        /**
         * 每次向EXCEL写入的记录数(查询每页数据大小) 20W
         */
        public static final Integer PER_WRITE_ROW_COUNT = 200000;
    
    }
    

    注: 为了书写方便,此处俩个必须要整除,可以省去很多不必要的判断。  另外如果自己测试,可以改为100,20。 

     

    2.4.1 数据量少的(20W以内吧):一个SHEET一次查询导出 

        @Override
        public ResultVO<Void> exportSysSystemExcel(SysSystemVO sysSystemVO, HttpServletResponse response) throws Exception {
    
            ServletOutputStream out = null;
            try {
                out = response.getOutputStream();
                ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
    
                // 设置EXCEL名称
                String fileName = new String(("SystemExcel").getBytes(), "UTF-8");
    
                // 设置SHEET名称
                Sheet sheet = new Sheet(1, 0);
                sheet.setSheetName("系统列表sheet1");
    
                // 设置标题
                Table table = new Table(1);
                List<List<String>> titles = new ArrayList<List<String>>();
                titles.add(Arrays.asList("系统名称"));
                titles.add(Arrays.asList("系统标识"));
                titles.add(Arrays.asList("描述"));
                titles.add(Arrays.asList("状态"));
                titles.add(Arrays.asList("创建人"));
                titles.add(Arrays.asList("创建时间"));
                table.setHead(titles);
    
                // 查数据写EXCEL
                List<List<String>> dataList = new ArrayList<>();
                List<SysSystemVO> sysSystemVOList = this.sysSystemReadMapper.selectSysSystemVOList(sysSystemVO);
                if (!CollectionUtils.isEmpty(sysSystemVOList)) {
                    sysSystemVOList.forEach(eachSysSystemVO -> {
                        dataList.add(Arrays.asList(
                                eachSysSystemVO.getSystemName(),
                                eachSysSystemVO.getSystemKey(),
                                eachSysSystemVO.getDescription(),
                                eachSysSystemVO.getState().toString(),
                                eachSysSystemVO.getCreateUid(),
                                eachSysSystemVO.getCreateTime().toString()
                        ));
                    });
                }
                writer.write0(dataList, sheet, table);
    
                // 下载EXCEL
                response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName).getBytes("gb2312"), "ISO-8859-1") + ".xls");
                response.setContentType("multipart/form-data");
                response.setCharacterEncoding("utf-8");
                writer.finish();
                out.flush();
    
            } finally {
                if (out != null) {
                    try {
                        out.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
    
            return ResultVO.getSuccess("导出系统列表EXCEL成功");
        }

     

    2.4.2 数据量适中(100W以内): 一个SHEET分批查询导出 

        @Override
        public ResultVO<Void> exportSysSystemExcel(SysSystemVO sysSystemVO, HttpServletResponse response) throws Exception {
    
            ServletOutputStream out = null;
            try {
                out = response.getOutputStream();
                ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
    
                // 设置EXCEL名称
                String fileName = new String(("SystemExcel").getBytes(), "UTF-8");
    
                // 设置SHEET名称
                Sheet sheet = new Sheet(1, 0);
                sheet.setSheetName("系统列表sheet1");
    
                // 设置标题
                Table table = new Table(1);
                List<List<String>> titles = new ArrayList<List<String>>();
                titles.add(Arrays.asList("系统名称"));
                titles.add(Arrays.asList("系统标识"));
                titles.add(Arrays.asList("描述"));
                titles.add(Arrays.asList("状态"));
                titles.add(Arrays.asList("创建人"));
                titles.add(Arrays.asList("创建时间"));
                table.setHead(titles);
    
                // 查询总数并 【封装相关变量 这块直接拷贝就行 不要改动】
                Integer totalRowCount = this.sysSystemReadMapper.selectCountSysSystemVOList(sysSystemVO);
                Integer pageSize = ExcelConstant.PER_WRITE_ROW_COUNT;
                Integer writeCount = totalRowCount % pageSize == 0 ? (totalRowCount / pageSize) : (totalRowCount / pageSize + 1);
    
                // 写数据 这个i的最大值直接拷贝就行了 不要改
                for (int i = 0; i < writeCount; i++) {
                    List<List<String>> dataList = new ArrayList<>();
    
                    // 此处查询并封装数据即可 currentPage, pageSize这个变量封装好的 不要改动
                    PageHelper.startPage(i + 1, pageSize);
                    List<SysSystemVO> sysSystemVOList = this.sysSystemReadMapper.selectSysSystemVOList(sysSystemVO);
                    if (!CollectionUtils.isEmpty(sysSystemVOList)) {
                        sysSystemVOList.forEach(eachSysSystemVO -> {
                            dataList.add(Arrays.asList(
                                    eachSysSystemVO.getSystemName(),
                                    eachSysSystemVO.getSystemKey(),
                                    eachSysSystemVO.getDescription(),
                                    eachSysSystemVO.getState().toString(),
                                    eachSysSystemVO.getCreateUid(),
                                    eachSysSystemVO.getCreateTime().toString()
                            ));
                        });
                    }
                    writer.write0(dataList, sheet, table);
                }
    
                // 下载EXCEL
                response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName).getBytes("gb2312"), "ISO-8859-1") + ".xls");
                response.setContentType("multipart/form-data");
                response.setCharacterEncoding("utf-8");
                writer.finish();
                out.flush();
    
            } finally {
                if (out != null) {
                    try {
                        out.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
    
            return ResultVO.getSuccess("导出系统列表EXCEL成功");
        }

     

    2.4.3 数据里很大(几百万都行): 多个SHEET分批查询导出 

        @Override
        public ResultVO<Void> exportSysSystemExcel(SysSystemVO sysSystemVO, HttpServletResponse response) throws Exception {
    
            ServletOutputStream out = null;
            try {
                out = response.getOutputStream();
                ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
    
                // 设置EXCEL名称
                String fileName = new String(("SystemExcel").getBytes(), "UTF-8");
    
                // 设置SHEET名称
                String sheetName = "系统列表sheet";
    
                // 设置标题
                Table table = new Table(1);
                List<List<String>> titles = new ArrayList<List<String>>();
                titles.add(Arrays.asList("系统名称"));
                titles.add(Arrays.asList("系统标识"));
                titles.add(Arrays.asList("描述"));
                titles.add(Arrays.asList("状态"));
                titles.add(Arrays.asList("创建人"));
                titles.add(Arrays.asList("创建时间"));
                table.setHead(titles);
    
                // 查询总数并封装相关变量(这块直接拷贝就行了不要改)
                Integer totalRowCount = this.sysSystemReadMapper.selectCountSysSystemVOList(sysSystemVO);
                Integer perSheetRowCount = ExcelConstant.PER_SHEET_ROW_COUNT;
                Integer pageSize = ExcelConstant.PER_WRITE_ROW_COUNT;
                Integer sheetCount = totalRowCount % perSheetRowCount == 0 ? (totalRowCount / perSheetRowCount) : (totalRowCount / perSheetRowCount + 1);
                Integer previousSheetWriteCount = perSheetRowCount / pageSize;
                Integer lastSheetWriteCount = totalRowCount % perSheetRowCount == 0 ?
                        previousSheetWriteCount :
                        (totalRowCount % perSheetRowCount % pageSize == 0 ? totalRowCount % perSheetRowCount / pageSize : (totalRowCount % perSheetRowCount / pageSize + 1));
    
    
                for (int i = 0; i < sheetCount; i++) {
    
                    // 创建SHEET
                    Sheet sheet = new Sheet(i, 0);
                    sheet.setSheetName(sheetName + i);
    
                    // 写数据 这个j的最大值判断直接拷贝就行了,不要改动
                    for (int j = 0; j < (i != sheetCount - 1 ? previousSheetWriteCount : lastSheetWriteCount); j++) {
                        List<List<String>> dataList = new ArrayList<>();
    
                        // 此处查询并封装数据即可 currentPage, pageSize这俩个变量封装好的 不要改动
                        PageHelper.startPage(j + 1 + previousSheetWriteCount * i, pageSize);
                        List<SysSystemVO> sysSystemVOList = this.sysSystemReadMapper.selectSysSystemVOList(sysSystemVO);
                        if (!CollectionUtils.isEmpty(sysSystemVOList)) {
                            sysSystemVOList.forEach(eachSysSystemVO -> {
                                dataList.add(Arrays.asList(
                                        eachSysSystemVO.getSystemName(),
                                        eachSysSystemVO.getSystemKey(),
                                        eachSysSystemVO.getDescription(),
                                        eachSysSystemVO.getState().toString(),
                                        eachSysSystemVO.getCreateUid(),
                                        eachSysSystemVO.getCreateTime().toString()
                                ));
                            });
                        }
                        writer.write0(dataList, sheet, table);
                    }
                }
    
                // 下载EXCEL
                response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName).getBytes("gb2312"), "ISO-8859-1") + ".xls");
                response.setContentType("multipart/form-data");
                response.setCharacterEncoding("utf-8");
                writer.finish();
                out.flush();
    
            } finally {
                if (out != null) {
                    try {
                        out.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
    
            return ResultVO.getSuccess("导出系统列表EXCEL成功");
        }

    三、总结

                造的假数据,100W条记录,18个字段,测试导出是70s。  在实际上产环境使用的时候,具体的还是要看自己写的sql的性能。 sql性能快的话,会很快。 有一点推荐一下: 在做分页的时候使用单表查询, 对于所需要处理的外键对应的冗余字段,在外面一次性查出来放到map里面(推荐使用@MapKey注解),然后遍历list的时候根据外键从map中获取对应的名称。一个宗旨:少发查询sql, 才能更快的导出。   

                题外话: 如果数据量过大,在使用count(1)查询总数的时候会很慢,可以通过调整mysql的缓冲池参数来加快查询,请参见博主的另一篇博文MYSQL单表数据量过大查询过慢配置优化innodb_buffer_pool_size。  还有就是遇到了一个问题,使用pagehelper的时候,数据量大的时候,limit 0,20W;  limit 20W,40W,  limit 40W,60W, limit 60W,80W 查询有的时候会很快,有的时候会很慢,待研究。

     

     

     

    展开全文
  • EasyExcel配置及使用

    千次阅读 2020-04-07 00:24:09
    EasyExcel 一、Excel导入导出的应用场景 1、数据导入:减轻录入工作量 2、数据导出:统计信息归档 3、数据传输:异构系统之间数据传输 二、EasyExcel简介 1、EasyExcel特点 Java领域解析、生成Excel比较有名的框架有...

    EasyExcel

    一、Excel导入导出的应用场景

    1、数据导入:减轻录入工作量
    2、数据导出:统计信息归档
    3、数据传输:异构系统之间数据传输

    二、EasyExcel简介

    1、EasyExcel特点

    Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
    EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
    EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。

    三、EasyExcel对Excel写操作

    1、创建一个普通的maven项目

    2、pom中引入xml相关依赖

    
    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>
    

    3、创建实体类

    在这里插入图片描述
    设置表头和添加的数据字段

    import com.alibaba.excel.annotation.ExcelProperty;
    
    //设置表头和添加的数据字段
    
    public class DemoData {
    
        //设置表头名称
    
        @ExcelProperty("学生编号")
        private int sno;
    
        //设置表头名称
        @ExcelProperty("学生姓名")
        private String sname;
    
        public int getSno() {
            return sno;
        }
        public void setSno(int sno) {
            this.sno = sno;
        }
        public String getSname() {
            return sname;
        }
        public void setSname(String sname) {
            this.sname = sname;
        }
        @Override
        public String toString() {
            return "DemoData{" +
                    "sno=" + sno +
                    ", sname='" + sname + '\'' +
                    '}';
        }
    }
    

    4 、实现写/读操作

    1、创建读取操作的监听器
    在这里插入图片描述

    //创建读取excel监听器
    public class ExcelListener extends AnalysisEventListener<ReadData> {
        //创建list集合封装最终的数据
        List<ReadData> list = new ArrayList<ReadData>();
        
        //一行一行去读取excle内容
        @Override
        public void invoke(ReadData user, AnalysisContext analysisContext) {
           System.out.println("***"+user);
            list.add(user);
        }
        //读取excel表头信息
        @Override
        public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
            System.out.println("表头信息:"+headMap);
        }
        //读取完成后执行
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        }
    }
    

    2、构建写/读操作
    在这里插入图片描述

    package codebuild.excel;
    
    import com.alibaba.excel.EasyExcel;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class TestEasyExcel {
    
        public static void main(String[] args) {
            //实现excel写的操作
            //1 设置写入文件夹地址和excel文件名称
            String filename = "/Users/hello/Downloads/excel/write.xlsx";
            //2 调用easyexcel里面的方法实现写操作
            //write方法两个参数:第一个参数文件路径名称,第二个参数实体类class
            EasyExcel.write(filename,DemoData.class).sheet("学生列表").doWrite(getData());
    
            //实现excel读操作
    //        String filename = "/Users/hello/Downloads/excel/write.xlsx";
            EasyExcel.read(filename,DemoData.class,new ExcelListener()).sheet().doRead();
        }
    
        //创建方法返回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("hello"+i);
                list.add(data);
            }
            return list;
        }
    }
    
    

    结果

    在这里插入图片描述

    在这里插入图片描述

    展开全文
  • JAVA使用easyexcel操作Excel

    万次阅读 多人点赞 2018-05-16 16:30:28
    之前写过一篇《JAVA操作Excel》,介绍了jxl和poi读写Excel的实现,今天为大家介绍一下使用easyexcel对Excel进行读写,项目主页地址:https://github.com/alibaba/easyexcel 作者对easyexcel的介绍是: Java解析...
  • alibaba/easyexcel 框架使用

    千次阅读 2019-06-20 18:15:04
    写Excel 没有模板 ExcelWriter writer = EasyExcelFactory.getWriter(out); //写第一个sheet, sheet1 数据全是List<String> 无模型映射关系 Sheet sheet1 = new Sheet(1, 3);...sheet1.setSheetName("第一个...
  • easyexcel代码量较小,使用简单,而且性能较佳,是一个非常好的选择。 源码地址:https://github.com/alibaba/easyexcel 工具类使用方法很简单: 1、maven依赖 <!-- ...
  • EasyExcel,用法

    千次阅读 2019-06-04 11:22:48
    阿里出品的 EasyExcel,安利一波 添加依赖 <!--alibaba easyexcel--> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version&...
  • EasyExcel

    千次阅读 2019-05-12 21:23:03
    三、阿里出品的 EasyExcel,安利一波 四、EasyExcel 解决了什么 五、快速上手 六、特殊场景支持 七、Web 下载示例代码 八、需要注意的点 九、总结 一、前言 关于导出 Excel 文件,可以说是大多数服务中都...
  • EasyExcel全面教程快速上手

    万次阅读 多人点赞 2020-05-19 23:46:19
    EasyExcel教程 本文使用的技术是Alibaba集团开源的EasyExcel技术,该技术是针对Apache POI技术的封装和优化,主要解决了POI技术的耗内存问题,并且提供了较好的API使用。不需要大量的代码就可以实现excel的操作功能...
  • easyExcel实现excel文件上传和下载

    万次阅读 2019-07-28 16:04:05
    一、easyExcel简介   在工作中,经常需要把excel中的数据导入系统,亦或是把系统中符合筛选条件的数据通过excel的方式导出。   Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的...
  • easyexcel使用问题处理

    万次阅读 2018-05-22 11:30:24
    项目中有处理excel文件需求,之前用过poi和jxl,两者处理文档的速度很快,但jxl无法处理07及以上版本的excel,而poi经常出现outofmemory错误,了解到阿里有一个开源的easyexcel可以解决poi中的oom问题,所以在项目中...
  • 使用EasyExcel的坑

    千次阅读 2019-08-30 14:29:53
    阿里的EasyExcel也是基于POI的,为啥不直接使用POI呢,因为现在客户Excel的数据量都比较大,一个Excel文件轻松超过10万条,直接使用POI很容易内存溢出,而EasyExcel通过异步加载的方式,一次取1000条(有限的条数)...
  • EasyExcel的使用 一、EasyExcel 1.EasyExcel简介 easyExcel是阿里巴巴开源poi插件之一,当前最新版本2.1.3,poi版本3.17 主要解决了poi框架使用复杂,sax解析模式不容易操作,数据量大起来容易OOM,解决了POI并发...
  • JAVA使用easyexcel导出excel

    千次阅读 2019-10-22 11:37:36
    JAVA使用easyexcel导出excel 导出excel的工具有很多,如poi,jxl等但是他们导出excel会比较的消耗内存,一旦数据大起来可能造成内存溢出,而easyexcel很好的避免了内存溢出的问题。 首先下载easyexcel的依赖包: ...
  •   本篇文章主要介绍一下使用阿里开源的Easyexcel工具处理读取excel文件,因为之前自己想在网上找一下这个简单的立即上手的博客,发现很多文章的教程都针对比较旧的版本的Easyexcel,没有使用新版本的方法,导致很...
  • Java解析excel工具easyexcel 助你快速简单避免OOM

    万次阅读 多人点赞 2018-07-19 15:04:27
    Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API...easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要10...
  • EasyExcel为单个Cell设置样式

    万次阅读 2019-08-20 21:02:16
    EasyExcel是阿里巴巴对POI封装的一个库,号称解决了POI的OOM问题,并且在使用上也更方便一些 Github: 然而我在使用的时候发现还是有很多坑,其中一个比较头疼的是对单个单元格样式的设置。EasyExcel提供了一个...
  • EasyExcel实现批量导入

    千次阅读 2019-08-23 10:09:33
    最近项目中遇到需要Excel导入数据的情况,由于几十万条的数据量过大,再加上逻辑处理比较多,最初采用poi导入,占用资源过大,导入过程中服务器内存几乎100%占用,耗时较久,因此更换为EasyExcel。实测效果确实...
  • 写在最前1.1 EasyExcel版本1.2 初探源码表头实体类 MyUser3. 最简单的导出Excel文件 1. 写在最前 不吹不黑,这玩意相比原生的poi来说,用起来确实方便。阿里开源的项目,github地址:...
1 2 3 4 5 ... 20
收藏数 3,661
精华内容 1,464
关键字:

easyexcel