POI 和 EasyExcel

前言

  1. 将表格信息导出为Excel表格(导出数)
  2. 将Excel表格信息录入到数据库(导入数据)

操作Excel目前比较流行的就是 Apache POI 和阿里巴巴的 EasyExcel

Apache POI

Apache POI 官网:https://poi.apache.org/

  • HSSF:Excel97-2003版本,扩展名为.xls。一个sheet最大行数65536,最大列数256。
  • XSSF:Excel2007版本开始,扩展名为.xlsx。一个sheet最大行数1048576,最大列数16384。
  • SXSSF:是在XSSF基础上,POI3.8版本开始提供的支持低内存占用的操作方式,扩展名为.xlsx。

Excel版本兼容性是向下兼容。

03版本和07版的Excel表的生成

java 复制代码
<!--xls(03版本)-->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.9</version>
</dependency>

<!--xlsx(07版本)-->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.9</version>
</dependency>

代码

java 复制代码
@Override
@CrossOrigin
public void testEasyExcel(Map<String, Object> map, HttpServletResponse response) throws IOException {
    // var sheetName = "工作表名"
    // var headList = ["表头1", "表头2"]
    // var filedList = ["字段名1", "字段名2"]
    // var dataList = [{字段名1: "2914"}, {字段名2: "2935"}, {字段名3: "2937"}]

    String sheetName = map.get("sheetName").toString();
    List<String> headList = (List)map.get("headList");
    List<String> filedList = (List)map.get("filedList");
    List<Map> dataList = (List)map.get("dataList");

    // 创建一个03版本的工作簿
    Workbook workbook = new HSSFWorkbook();
    // 创建一个07版本的工作簿
    // Workbook workbook = new XSSFWorkbook();
    //
    // Workbook workbook = new SXSSFWorkbook();
    // 创建一个工作表,指定表名
    Sheet sheet = workbook.createSheet(sheetName);
    // 创建一个行
    Row row = sheet.createRow(0);
    // 设置行高
    row.setHeight((short)450);
    // 设置样式
    HSSFCellStyle cellStyle = (HSSFCellStyle)workbook.createCellStyle();
    // XSSFCellStyle cellStyle = (XSSFCellStyle) workbook.createCellStyle();
    // 设置背景颜色
    cellStyle.setFillBackgroundColor(IndexedColors.SEA_GREEN.getIndex());
    // 设置居中
    // cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
    // 设置字体
    HSSFFont font = (HSSFFont)workbook.createFont();
    // XSSFFont font = (XSSFFont)workbook.createFont();
    font.setFontName("宋体");
    font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
    font.setFontHeightInPoints((short)12);
    cellStyle.setFont(font);

    // 写入表头
    for (int i = 0; i < headList.size(); i++) {
        row.createCell(i).setCellValue(headList.get(i));
        row.getCell(i).setCellStyle(cellStyle);
    }

    // 写入字段名和写入数据
    for (int i = 0; i < dataList.size(); i++) {
        row = sheet.createRow(i + 1);
        row.setHeight((short)450);
        Map<String, String> datamap = dataList.get(i);
        for (int j = 0; j < filedList.size(); j++) {
            String value = String.valueOf(datamap.get(filedList.get(j)));
            if (value != null && !"null".equals(value)) {
                row.createCell(j).setCellValue(value);
            }
        }
    }

    // 文件扩展名
    response.setContentType("application/vnd.ms-excel;charset=utf-8");
    OutputStream os = response.getOutputStream();
    // 输出
    workbook.write(os);
    // 刷新
    os.flush();
    // 关闭
    os.close();
    // 清除临时文件
	// ((SXSSFWorkbook)workbook).dispose();
}

03版本和07版的Excel表的读取

java 复制代码
@Override
    @CrossOrigin
    public void testEasyExcel(Map<String, Object> map, HttpServletResponse response) throws IOException {

        // 获取文件流 03 xls 07 xlsx
        FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Desktop\\工号批量导入模板.xls");
        // 读取excel
        Workbook workbook = new HSSFWorkbook(fileInputStream);
        // 拿到工作表
        Sheet sheet = workbook.getSheetAt(0);
        // 获取标题行内容
        Row rowTitle = sheet.getRow(1);
        if (rowTitle != null) {
            // 获取这行有多少列
            int cellCount = rowTitle.getPhysicalNumberOfCells();
            // 循环得到这一行的所有数据
            for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                Cell cell = rowTitle.getCell(cellNum);
                if (cell != null) {
                    int cellType = cell.getCellType();
                    // 这里标题行数据的类型全部为字符串
                    String stringCellValue = cell.getStringCellValue();
                    System.out.print(stringCellValue + " | ");
                }
            }
            System.out.println();
        }

        // 获取表中的内容,即获取除了第一行的内容,取表中总共有多少行
        int physicalNumberOfRows = sheet.getPhysicalNumberOfRows();
        // 从第二行开始循环,即获取除了第一行的内容
        for (int rowNum = 2; rowNum < physicalNumberOfRows; rowNum++) {
            Row rowData = sheet.getRow(rowNum);
            if (rowData != null) {
                // 根据标题行获取多少列
                int cellCount = rowTitle.getPhysicalNumberOfCells();
                // 循环得到这一行的所有数据
                for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                    Cell cell = rowData.getCell(cellNum);
                    if (cell != null) {
                        int cellType = cell.getCellType();
                        String cellValue = "";
                        // 匹配列的数据类型
                        switch (cellType) {
                            // 字符串
                            case HSSFCell.CELL_TYPE_STRING:
                                cellValue = cell.getStringCellValue();
                                break;
                            // 布尔值
                            case HSSFCell.CELL_TYPE_BOOLEAN:
                                cellValue = String.valueOf(cell.getBooleanCellValue());
                                break;
                            // 空
                            case HSSFCell.CELL_TYPE_BLANK:
                                break;
                            // 数字(数字又分为日期和普通数字)
                            case HSSFCell.CELL_TYPE_NUMERIC:
                                // 如果为日期
                                if (HSSFDateUtil.isCellDateFormatted(cell)) {
                                    Date date = cell.getDateCellValue();
                                    cellValue = new DateTime(date).toString("yyyy-MM-dd");
                                } else {
                                    // 不是日期格式的话,需要防止数字过长,转换为字符串输出
                                    cell.setCellType(HSSFCell.CELL_TYPE_STRING);
                                    cellValue = cell.toString();
                                }
                                break;
                            // 错误的数据类型
                            case HSSFCell.CELL_TYPE_ERROR:
                                break;
                        }
                        System.out.print(cellValue + " | ");
                    }
                }
                System.out.println();
            }
        }
        fileInputStream.close();
    }


通过 Blob 下载文件

前端的各种后台管理系统中,往往都会存在大量数据的展示,分析等,一般这种项目都会有数据导出功能,在开发中,一般都是后端返回Blob数据类型,也就是一个二进制对象,那么本篇文章就是讲解前端拿到Blob数据后该怎么处理。

这里简单说一下Blob,全称Binary large Object,二进制大对象(BLOB)是一种可以存储二进制对象或数据的数据类型。

前端中Blob对象的构造函数语法:

java 复制代码
new Blob(array, options)

参数 array 是一个数据序列即数组,可以是任意格式的值,例如,任意数量的字符串,Blobs 以及 ArrayBuffers。

参数 options 用于指定将要放入Blob中的数据的类型(MIME)

  1. 在封装请求的时候一定要记得加上responseType。
java 复制代码
/**
  * 导出exportExcel
  * @param data
  */
 public static exportExcel(data) {
     return axios({
         url: ``,
         method: 'get',
         params: data,
         responseType: "blob"//定义好返回的数据类型
     });
 }
  1. 拿到Blob数据类型转化为文件并下载。
java 复制代码
/**
 * 导出文件下载方法
 * @param data 这个参数就是从接口返回的Blob二进制文件流
 */
const exportFile= (data) => {
      const blob = new Blob([data], {type: "application/vnd.ms-excel;charset=utf-8"});
      const fileName = "文件名" + new Date().getTime() + ".xls";//我这里是文件名加上时间,可以根据自己需求来
      const elink = document.createElement("a"); // 创建a标签
      elink.download = fileName; // 为a标签添加download属性  //命名下载名称
      elink.style.display = "none";
      elink.href = URL.createObjectURL(blob);
      document.body.appendChild(elink);
      elink.click(); // 点击下载
      URL.revokeObjectURL(elink.href); // 释放URL 对象
      document.body.removeChild(elink); // 释放标签
}

EasyExcel

easyExcel 官网:https://easyexcel.opensource.alibaba.com/

EasyExcel 是阿里巴巴开源的一个excel处理框架、以使用简单、节省内存著称,EasyExcel 能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

下图是EasyExcel和POI在解析Excel时的对比图

代码

java 复制代码
@Override
@CrossOrigin
public void testEasyExcel(Map<String, Object> map, HttpServletResponse response) throws IOException {
    String fileName = "C:\\Users\\13992\\Desktop\\EasyTest.xlsx";
    // 这里需要指定写用哪个class去写,然后写到第一个sheet,名字为模板然后文件流会自动关闭
    // 如果这里想使用03 则 传入excelType参数即可
    List<Map> list = (List<Map>) map.get("dataList");
    List<DemoData> demoList = ListUtils.newArrayList();
    for(int i=0;i<list.size();i++){
        DemoData data = new DemoData();
        data.setStaffCode(list.get(i).get("staffCode").toString());
        data.setStaffName(list.get(i).get("staffName").toString());
        demoList.add(data);
    }
    EasyExcel.write(fileName, DemoData.class).sheet(map.get("sheetName").toString())// 工作表名
        .doWrite(demoList);
}

结论

POI存在的问题:非常的消耗内存,EasyExcel 遇到再大的excel都不会出现内存溢出的问题,能够将一个原本3M的excel文件,POI来操作将会占用内存100M,使用EasyExcel降低到几KB,使用起来更加简单。而easyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

相关推荐
二十七剑2 分钟前
jvm中各个参数的理解
java·jvm
东阳马生架构1 小时前
JUC并发—9.并发安全集合四
java·juc并发·并发安全的集合
计算机小白一个2 小时前
蓝桥杯 Java B 组之岛屿数量、二叉树路径和(区分DFS与回溯)
java·数据结构·算法·蓝桥杯
菠菠萝宝2 小时前
【Java八股文】10-数据结构与算法面试篇
java·开发语言·面试·红黑树·跳表·排序·lru
不会Hello World的小苗2 小时前
Java——链表(LinkedList)
java·开发语言·链表
Allen Bright3 小时前
【Java基础-46.3】Java泛型通配符详解:解锁类型安全的灵活编程
java·开发语言
柃歌3 小时前
【UCB CS 61B SP24】Lecture 7 - Lists 4: Arrays and Lists学习笔记
java·数据结构·笔记·学习·算法
柃歌3 小时前
【UCB CS 61B SP24】Lecture 4 - Lists 2: SLLists学习笔记
java·数据结构·笔记·学习·算法
是姜姜啊!3 小时前
redis的应用,缓存,分布式锁
java·redis·spring
梨落秋溪、3 小时前
输入框元素覆盖冲突
java·服务器·前端