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时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

相关推荐
开开心心就好11 分钟前
本地文件搜索工具,双击Ctrl就能呼出搜索
java·服务器·前端·支持向量机·ffmpeg·音视频·模拟退火算法
独隅16 分钟前
Claude 高效使用指南深度评测
java·开发语言·claude·claude code
ByteCoder.23 分钟前
班前十分钟的清单,按体检引导单来写
java·服务器·前端·助播虾
海的透彻25 分钟前
如何让docker容器启动时用自定义的用户
java·docker·dubbo
疯狂学习GIS30 分钟前
将谷歌地球引擎账号升级为Contributor与Partner层级的方法
java·数据库·人工智能
GG.WinGel41 分钟前
质数数目(蓝桥杯练习题)
java·数据结构·算法·青少年编程·蓝桥杯
泡泡糖的中文规格书1 小时前
HDLbits 2.2 学习笔记 · 多路复用器
java·开发语言
John_Hui1 小时前
第 3 篇:配置 JDK 17 环境
java·开发语言·ubuntu
秋天的怀念_David1 小时前
算法一千零一夜 · 第十七夜 长街点灯
java·程序人生·算法
不吃鱼的羊1 小时前
如何应对恐惧
java