POI导入导出、EasyExcel批量导入和分页导出

news2024/12/21 9:46:28

文件导入导出POI、EasyExcel

POI:消耗内存非常大,在线上发生过堆内存溢出OOM;在导出大数据量的记录的时候也会造成堆溢出甚至宕机,如果导入导出数据量小的话还是考虑的,下面简单介绍POI怎么使用

POI导入

首先拿到文件对象,这个对象可以是前端上传的或者从目录读取的,或者OOS里面下载的。
下面是关于POI导入的核心代码,非常的简单。

// 把文件转为Workbook 对象,这样就能任意操作它了
Workbook workbook = new XSSFWorkbook(file.getInputStream());
// 获取第0页的数据
Sheet sheet = workbook.getSheetAt(0);
// 获取第一行
int firstRowNum = sheet.getFirstRowNum();
// 获取最后一行
int lastRowNum = sheet.getLastRowNum();
// 获取第一行数据-即表头
// 如果需要用到表头的话,可以调用Row对象里面的getCell取值
Row head = sheet.getRow(firstRowNum);
// 获取第一列
short firstCellNum = head.getFirstCellNum();
// 获取最后一列
short lastCellNum = head.getLastCellNum();
// 遍历所有数据行-跳过表头
 for (int rowIndex = firstRowNum + 1; rowIndex <= lastRowNum; rowIndex++){
      // 获取数据行
		 Row dataRow = sheet.getRow(rowIndex);
		 // 接下来遍历这一行的数据
		  for (int cellIndex = firstCellNum; cellIndex < lastCellNum; cellIndex++) {
		       // 获取单元格
		  		Cell cell = dataRow.getCell(cellIndex);
		  		// 把单元格数据转为String
		  		System.out.println(cell.toString());
		  }
 }

POI导出

导出功能的话一般会从数据库查出来数据,导入到Excel表中
下面是关于POI导出的核心代码,非常的简单。

   // 创建工作簿
   Workbook wb = new XSSFWorkbook();
   // 创建页
   Sheet sheet = wb.createSheet("Sheet1");
   // 创建表头
   Row tableHeadRow = sheet.createRow(0);
   // 下面就是对表头与行添加数据了
   // 创建表头的单元格
   int headIndex = 0;
   // 这个列数自己控制-可以通过对象的字段数控制,通过反射机制获取对象信息
   for (int i = 0; i < 10; i++) {
        // 创建单元格
        Cell cell = tableHeadRow.createCell(headIndex++);   
          // 设置值-这里的值是自己对象的值
        cell.setCellValue("表头列"+i);
   }
   // 现在表头设置好了,接下来设置数据行
   // 这里从第一行开始,第0行是表头
     for (int i = 1; i < 20; i++) {
         Row dataRow= sheet.createRow(i);
         // 为数据行填充数据,这里的列数和表头的列数一致就行
         for (int i = 0; i < 10; i++) {
          // 创建单元格
          Cell cell = dataRow.createCell(index ++);   
          // 设置值-这里的值是自己对象的值
          cell.setCellValue("数据行的数据"+i);
      }
     }
  
  
   

EasyExcel导入

EasyExcel是阿里巴巴开源的框架,它是对POI进行了封装,解决了POI耗内存的痛点。
使用EasyExcel步骤比较多,但是却能帮助我们更加灵活的开发。
1、实体类上加注解@ExcelProperty(“日期”),日期是表头名

    @ExcelProperty("日期")
    private String prodDate;

2、EasyExcel的监听器,实现ReadListener接口,这里的泛型为实体类

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
public class ImportWRPListenner implements ReadListener<WorkRollPlan> {
    private List<WorkRollPlan> list = new ArrayList<>();

    /**
     * 每读一行触发一次*
     *
     * @param workRollPlan
     * @param analysisContext
     */
    @Override
    public void invoke(WorkRollPlan workRollPlan, AnalysisContext analysisContext) {
        // 每次触发把workRollPlan放进列表
        list.add(workRollPlan);
    }
    /**
     * 所有数据读完之后触发一次*
     *
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

3、使用

public String uploadExcel(MultipartFile[] files) throws IOException {
        ImportWRPListenner importWRPListenner = new ImportWRPListenner();
        for (int i = 0; i < files.length; i++) {
            MultipartFile file = files[i];
            InputStream inputStream = file.getInputStream();
            EasyExcel.read(inputStream, WorkRollPlan.class, importWRPListenner)
                    .sheet(0) // 读第0页
                    .headRowNumber(1) // 表头占1几行
                    .doRead();
        }
        return "ok";
    }

4、如何入库
在doAfterAllAnalysed方法里把list 入库,入库需要有Service对象,而Listenner是不被Spring管理的,所以说不能通过注入的方式获取Service。
方法一:通过容器获取对象,下面是获取Spring容器的工具类。

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * spring工具类 方便在非spring管理环境中获取bean
 * 
 * @author ruoyi
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware 
{
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException 
    {
        SpringUtils.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
    {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     *
     * @param clz
     * @return
     * @throws BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException
    {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name)
    {
        return beanFactory.containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getAliases(name);
    }

    /**
     * 获取aop代理对象
     * 
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    {
        return (T) AopContext.currentProxy();
    }

    /**
     * 获取当前的环境配置,无配置返回null
     *
     * @return 当前的环境配置
     */
    public static String[] getActiveProfiles()
    {
        return applicationContext.getEnvironment().getActiveProfiles();
    }

    /**
     * 获取当前的环境配置,当有多个环境配置时,只获取第一个
     *
     * @return 当前的环境配置
     */
    public static String getActiveProfile()
    {
        final String[] activeProfiles = getActiveProfiles();
        return "StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null";
    }

    /**
     * 获取配置文件中的值
     *
     * @param key 配置文件的key
     * @return 当前的配置文件的值
     *
     */
    public static String getRequiredProperty(String key)
    {
        return applicationContext.getEnvironment().getRequiredProperty(key);
    }
}

通过Spring获取Service

 private WorkRollPlanService workRollPlanService = SpringUtils.getBean(WorkRollPlanService.class);

方法2:构建监听器的时候把Service传进来

 private WorkRollPlanService workRollPlanService;

    public ImportWRPListenner(WorkRollPlanService workRollPlanService) {
        this.workRollPlanService = workRollPlanService;
    }

在Service里面使用

ImportWRPListenner importWRPListenner = new ImportWRPListenner(this);

拿到Service之后在监听器的doAfterAllAnalysed方法入库就OK了。

5、如何导入指定条数就插入数据库。

public class ImportWRPListenner implements ReadListener<WorkRollPlan> {
	// 计数器
    public static int count = 0;

    private WorkRollPlanService workRollPlanService;

    public ImportWRPListenner(WorkRollPlanService workRollPlanService) {
        this.workRollPlanService = workRollPlanService;
    }

    private List<WorkRollPlan> list = new ArrayList<>(2000);

    /**
     * 每读一行触发一次*
     *
     * @param workRollPlan
     * @param analysisContext
     */
    @Override
    public void invoke(WorkRollPlan workRollPlan, AnalysisContext analysisContext) {
        // 每次触发把workRollPlan放进列表
        list.add(workRollPlan);
        count++;
        // 一旦计数器到达2000或者2000的倍数时,插入数据库,并清空list,释放内存
        if (count % 2000 == 0) {
            workRollPlanService.preUpload(list);
            list.clear();
        }
    }

    /**
     * 所有数据读完之后触发一次*
     *
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 最后不满2000条的数据在这里插入数据库
        workRollPlanService.preUpload(list);
    }
}

EasyExcel导出

public void WorkRollPlansToFileStream(HttpServletResponse response, List<WorkRollPlan> workRollPlans) throws IOException {
		response.setHeader("Content-disposition", "attachment;");
    OutputStream output = response.getOutputStream();
		 EasyExcel.write(output)
                .head(WorkRollPlan.class)
                .excelType(ExcelTypeEnum.XLSX)
                .sheet("Sheet1")
                .doWrite(workRollPlans);
        output.close();
}

看下效果
在这里插入图片描述
把id也导出了,这是我们不需要的。
需要在实体类上加注解

@ExcelIgnoreUnannotated

EasyExcel结合Mybatis plus的分页功能进行分页导出

工具类

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Builder;
import lombok.Data;
import org.springframework.http.HttpHeaders;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

public class EasyExcelUtil {
    @Data
    @Builder
    public static class ExcelParam {
        /**
         * 查询 Mapper
         */
        private BaseMapper baseMapper;

        /**
         * Lambda查詢方式
         */
        private LambdaQueryWrapper lambdaQueryWrapper;

        /**
         * 页码,默认从1开始
         */
        private Integer pageNo = 1;

        /**
         * 分页条数,,默认每个sheet 1000 条数据
         */
        private Integer pageSize = 1000;

        /**
         * 用于存放查询到的結果,让Excel生成
         */
        private Class<?> respClazz;

        /**
         * 生成的Excel 名称,不加后缀
         */
        private String fileName;

        /**
         * Excel sheet名称
         */
        private String sheetName;
    }

    public static void exportExcel(ExcelParam excelParam, HttpServletResponse response) {
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + excelParam.getFileName() + ".xlsx");
        try (
                ServletOutputStream outputStream = response.getOutputStream();
                ExcelWriter excelWriter = EasyExcel.write(outputStream, excelParam.getRespClazz()).build();
        ) {
            Page page = new Page(excelParam.getPageNo(), excelParam.getPageSize());
            page = (Page) excelParam.getBaseMapper().selectPage(page, excelParam.getLambdaQueryWrapper());
            /** 构建 */
            WriteSheet writeSheet1 = EasyExcel.writerSheet(1, excelParam.getSheetName() + "第" + excelParam.getPageNo() + "页").build();
            /** 获取总数 */
            Long totalPage = page.getPages();
            List records = page.getRecords();
            /** 写入内容 */
            excelWriter.write(records, writeSheet1);
            writeSheet1 = null; // GC
            // 若为空表
            if (CollUtil.isEmpty(page.getRecords())) {
                /** 生成完毕 */
                excelWriter.finish();
                /** 立即刷回 */
                outputStream.flush();
                return;
            }
            for (int i = excelParam.pageNo + 1, index = 2; i <= totalPage; i++, index++) {
                /** 清空*/
                records.clear();
                WriteSheet writeSheet = EasyExcel.writerSheet(index, excelParam.getSheetName() + "第" + i + "页").build();
                page.setCurrent(i);
                /** 新的查询 */
                page = (Page) excelParam.getBaseMapper().selectPage(page, excelParam.getLambdaQueryWrapper());
                records = page.getRecords();
                /** 输入内容內容 */
                excelWriter.write(records, writeSheet);
            }
            /** 生成完毕 */
            excelWriter.finish();
            /** 立即刷回 */
            outputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

使用

EasyExcelUtil.ExcelParam p = EasyExcelUtil.ExcelParam.builder()
                .baseMapper(workRollPlanService.getBaseMapper())           // 传Service类的BaseMapper进去
                .lambdaQueryWrapper(new LambdaQueryWrapper<WorkRollPlan>() // 构建查询条件
                        .eq(true, WorkRollPlan::getIsDeleted, 0)
                )
                .pageNo(1)                     // 数据从第一行开始,如果表头只有一行就是1,表头有两行的话就写2
                .respClazz(WorkRollPlan.class) // 这里写实体类
                .pageSize(1000)                // Excel每页数据大小
                .fileName("test")              // 文件名
                .sheetName("测试sheet")        // 页名
                .build();

        EasyExcelUtil.exportExcel(p, response);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/371322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java:如何选择一个Java API框架

Java编程语言是一种高级的、面向对象的语言&#xff0c;它使开发人员能够创建健壮的、可重用的代码。Java以其可移植性和平台独立性而闻名&#xff0c;这意味着Java代码可以在任何支持Java运行时环境(JRE)的系统上运行。Java和Node js一样&#xff0c;是一种功能强大的通用编程…

机试指南

文章目录零、绪论和IDE安装int取值范围常犯的编程小错误一、枚举和模拟 (暴力求解)(一) 枚举1.Reverse函数 求 反序数2.程序出错的原因1.编译错误 (compile)&#xff1a;基本语法错误2.链接错误 (link)&#xff1a;函数名写错了3.运行错误 (run)&#xff1a;结果与预期不符&…

前后端分离开发Springboot+VUE学习笔记

学习内容来源&#xff1a;传送门 目录前后端分离实现技术创建vue项目在idea中打开新建页面创建SpringBoot应用创建实体对象与数据库表元素绑定创建实体类接口前端调用数据跨域传输在springboot中解决总结前后端分离 前后端分离就是将一个应用的前端和后端代码分开写&#xff0…

前端:分享JS中7个高频的工具函数

目录 ◆1、将数字转换为货币 ◆2、将 HTML 字符串转换为 DOM 对象 ◆3、防抖 ◆4、日期验证 ◆5、将 FormData&#xff08;表单数据&#xff09;转换为 JSON ◆6、衡量一个函数的性能 ◆7、从数组中删除重复项 JavaScript 实用函数是有用的、可重复使用的片段&#xff0…

STM32开发(14)----CubeMX配置ADC

CubeMX配置ADC前言一、什么是ADC&#xff1f;二、实验过程1.单通道ADC采集STM32CubeMX配置代码实现2.多通道ADC采样(非DMA)STM32CubeMX配置代码实现3.多通道ADC采样&#xff08;DMA&#xff09;STM32CubeMX配置代码实现总结前言 本章介绍使用STM32CubeMX对ADC进行配置的方法&a…

SpringCloud之Seata(二)

4.Seata如何应用于项目&#xff1f; 安装seata及修改配置 4.1 官网下载Seata安装包 4.2 修改seata/config.txt 4.2.1 修改存储方式 store.db.dbTypemysql store.db.driverClassNamecom.mysql.jdbc.Driver store.db.urljdbc:mysql://你的IP:3306/seata?useUnicodetrue sto…

第一篇博客------自我介绍篇

目录&#x1f506;自我介绍&#x1f506;学习目标&#x1f506;如何学习单片机Part 1 基础理论知识学习Part 2 单片机实践Part 3 单片机硬件设计&#x1f506;希望进入的公司&#x1f506;结束语&#x1f506;自我介绍 Hello!!!我是一名即已经步入大二的计算机小白。 --------…

F4—LVDS接口LCD显示彩图测试-2023-02-25

1.简介 系列文章TFT彩条测试介绍到&#xff0c;屏幕是由厂家提供的TFT显示模组和屏幕PCB背板组成。PCB的作用是提供LCD背光所需的电压、用于屏幕显示的电压、与其他设备相连的排针或者其他连接器形式。当模组支持触摸功能时还可以接上触摸转换或触摸控制芯片&#xff0c;通过SP…

Qt 中的XML

XML的基本介绍&#xff1a; 在前端开发中&#xff1a;HTML是用来显示数据&#xff0c;而XML是用来传输和存储数据的 XML 指可扩展标记语言&#xff08;EXtensible Markup Language&#xff09;XML 是一种标记语言&#xff0c;很类似 HTMLXML 的设计宗旨是传输数据&#xff0c;而…

超简单的待办事项列表管理器todo

什么是 todo ? todo 是一个自托管的 todo web 应用程序&#xff0c;可让您以简单且最少的方式跟踪您的 todo。&#x1f4dd; 老苏觉得和之前介绍的 KissLists 比较像 文章传送门&#xff1a;最简单的共享列表服务器KissLists 官方提供了 Demo 演示站点&#xff1a;https://tod…

零基础的人如何入门 Python ?看完这篇文章你就懂了

第一部分&#xff1a;编程环境准备 零基础入门Python的话我不建议用IDE&#xff0c;IDE叫集成开发环境&#xff0c;这东西一般是专业程序员用来实战开发用的&#xff0c;好处很多&#xff0c;比如&#xff1a;调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单元测…

Android-MVVM之快速上手ViewModel

Android-MVVM之快速上手ViewModel什么是ViewModel&#xff1f;ViewModel生命周期&#xff1f;ViewModel的使用&#xff1f;什么是ViewModel&#xff1f; 简单来说&#xff0c;就是让view层(视图层)与model层(数据层)分离开来的桥梁。让view层展示ui&#xff0c;不持有数据。 Vi…

【2021春节】解题领红包之番外篇

【2021春节】解题领红包之番外篇前言原始代码解题思路flag1寻找flag2的寻找前言 记录下jsfuck的另类&#xff0c;时间都过去两年了&#xff0c;确实有点久远。。。 原始代码 要求找出flag1和flag2值 ([][])[([][[]][])[!![]](!![][][(![][])[[]](![][])[!![]!![]](![][])[!![…

Vue基础入门讲义(二)-语法基础

文章目录1.vue入门案例1.1.HTML模板1.2.vue渲染1.3.双向绑定1.4.事件处理2.Vue实例2.1.创建Vue实例2.2.模板或元素2.3.数据2.4.方法3.生命周期钩子3.1.生命周期3.2.钩子函数3.3.this1.vue入门案例 1.1.HTML模板 在项目目录新建一个HTML文件 01-demo.html 1.2.vue渲染 01-d…

SSM框架-AOP概述、Spring事务

16 spring整合mybatis 16.1 前情代码 实体类 public class Account {private Integer id;private String name;private Double money;public Integer getId() {return id;}public void setId(Integer id) {this.id id;}public String getName() {return name;}public void …

搭建k8s高可用集群—20230225

文章目录多master&#xff08;高可用&#xff09;介绍高可用集群使用技术介绍搭建高可用k8s集群步骤1. 准备环境-系统初始化2. 在所有master节点上部署keepalived3.1 安装相关包3.2 配置master节点3.3 部署haproxy错误解决3. 所有节点安装Docker/kubeadm/kubelet4. 部署Kuberne…

对redis之键值型数据库的理解

键值数据库&#xff0c;首先就要考虑里面可以存什么样的数据&#xff0c;对数据可以做什么样的操作&#xff0c;也就是数据模型和操作接口。它们看似简单&#xff0c;实际上却是我们理解 Redis 经常被用于缓存、秒杀、分布式锁等场景的重要基础。理解了数据模型&#xff0c;你就…

2022年网络安全竞赛——数字取证调查attack.pcapng

攻击日志分析:需求环境可私信博主获取 任务环境说明: 服务器场景:PYsystem0031服务器场景操作系统:未知服务器场景FTP用户名:anonymous 密码:空从靶机服务器的FTP上下载attack.pcapng数据包文件,通过分析数据包attack.pcapng,找出黑客的IP地址,并将黑客的IP地址作为FL…

SPI协议介绍

SPI协议介绍 文章目录SPI协议介绍一、 SPI硬件知识1.1 硬件连线1.2 SPI控制器内部结构二、 SPI协议2.1 传输示例2.2 SPI模式致谢一、 SPI硬件知识 1.1 硬件连线 引脚含义如下&#xff1a; 引脚含义DO(MOSI)Master Output, Slave Input&#xff0c;SPI主控用来发出数据&#x…

逆向之Windows PE结构

写在前面 对于Windows PE文件结构&#xff0c;个人认为还是非常有必要掌握和了解的&#xff0c;不管是在做逆向分析、免杀、病毒分析&#xff0c;脱壳加壳都是有着非常重要的技能。但是PE文件的学习又是一个非常枯燥过程&#xff0c;希望本文可以帮你有一个了解。 PE文件结构…