Spring Boot集成EasyPoi实现导入导出操作

文章目录

  • Spring Boot集成EasyPoi实现导入导出操作
    • 0 简要说明
    • 1 环境搭建
      • 1.1 项目目录
      • 1.2 依赖管理
      • 2.3 关于swagger处理
      • 2.4 关于切面处理耗时
        • 1 自定义注解
        • 2 定义切面类
        • 3 如何使用
      • 2.5 核心导入操作
      • 2.6 核心导出操作
    • 2 最佳实线
      • 2.1 导入操作
        • 1 实体类说明
        • 2 业务层
        • 3 效果
        • 3 控制层
      • 2.2 导出操作
      • 2.3 大数据量处理
    • 4 问题
      • 4.1 日期类型字段特殊处理
      • 4.2 文件上传
      • 4.3 指定导出路径

参考博客
1.官网

Spring Boot集成EasyPoi实现导入导出操作

0 简要说明

Excel自适应xls和xlsx两种格式,word只支持docx模式
主要特点

1.设计精巧,使用简单
2.接口丰富,扩展简单
3.默认值多,write less do more
4.spring mvc支持,web导出可以简单明了

在这里插入图片描述

1 环境搭建

1.1 项目目录

在这里插入图片描述

1.2 依赖管理

        <!--Spring 环境--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>xerces</groupId><artifactId>xercesImpl</artifactId><version>2.9.1</version></dependency><!--MySQL 驱动包--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.31</version></dependency><!--自动配置--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!--实体类优化--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--接口平台--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>2.0.7</version></dependency><!--commons-lang3工具包--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><!--Mybatis Plus 扩展包--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-extension</artifactId><version>3.5.1</version></dependency><!--Mybatis Plus 依赖包--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><!--easypoi--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.4.0</version></dependency><!--easyexcel--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.9.1</version></dependency><!--接口平台--><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency>

2.3 关于swagger处理

依赖

        <!--接口平台--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>2.0.7</version></dependency><!--接口平台--><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency>

配置类

package com.geekmice.springbootselfexercise.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;/*** @BelongsProject: spring-boot-scaffold* @BelongsPackage: com.geekmice.sbhelloworld.com.geekmice.sbpagehelper.config* @Author: pingmingbo* @CreateTime: 2023-07-30  15:45* @Description: TODO* @Version: 1.0*/
@Configuration
public class Knife4jConfig {@Bean(value = "defaultApi2")public Docket customDocket() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.basePackage("com.geekmice.springbootselfexercise.controller")).build();}/*** 构建 api文档的详细信息函数* @return*/private ApiInfo apiInfo() {return new ApiInfoBuilder().title("现货交易").version("1.0.0").description("现货交易详情").contact(new Contact("geekmice","http://geekmice.cn","2437690868@qq.com")).build();}
}

访问地址:http://localhost:端口号/doc.html
在这里插入图片描述

2.4 关于切面处理耗时

1 自定义注解

package com.geekmice.springbootselfexercise.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author PMB*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodExporter {
}

2 定义切面类

package com.geekmice.springbootselfexercise.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;/*** @BelongsProject: spring-boot-scaffold* @BelongsPackage: com.geekmice.com.geekmice.sbpagehelper.utils* @Author: pmb* @CreateTime: 2023-07-26  21:52* @Description: TODO* @Version: 1.0*/
@Aspect
@Component
@Slf4j
public class MethodExporterAspect {@Around("@annotation(com.geekmice.springbootselfexercise.annotation.MethodExporter)")public Object methodExporter(ProceedingJoinPoint point) throws Throwable {long start = System.currentTimeMillis() / 1000;Object proceed = point.proceed();long end = System.currentTimeMillis() / 1000;log.info("【耗时:】{}s", (end - start));return proceed;}
}

3 如何使用

直接在控制层方法上面添加注解,调用接口即可生效

    @PostMapping(value = "uploadFileByEasyPoi")@MethodExporter@ApiOperation(value = "通过easypoi上传文件")public AjaxResult uploadFileByEasyPoi(@RequestPart MultipartFile file){userService.uploadFileByEasyPoi(file);return AjaxResult.success();}

在这里插入图片描述

2.5 核心导入操作

    @Overridepublic void uploadFileByEasyPoi(MultipartFile file) {InputStream inputStream;try {inputStream = file.getInputStream();} catch (IOException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);}try {List<UserDomain> result = ExcelImportUtil.importExcel(inputStream, UserDomain.class, new ImportParams());for (UserDomain userDomain : result) {log.info("导入数据:【{}】", userDomain.toString());}this.saveBatch(result);log.info("录入结束");} catch (Exception e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);}}

2.6 核心导出操作

ExcelExportUtil是easypoi里面工具类

    public void downloadFileByEasyPoi() {List<UserDomain> list = new ArrayList(16);for (int i = 0; i < 10; i++) {UserDomain item = UserDomain.builder().userName(RandomStringUtils.randomAlphabetic(6)).address(RandomStringUtils.randomAlphabetic(10)).birthday(new Date()).sex("男").build();list.add(item);}Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), UserDomain.class, list);FileOutputStream outputStream;try {outputStream = new FileOutputStream("D:\\easypoi.xlsx");workbook.write(outputStream);} catch (FileNotFoundException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);} catch (IOException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);}}

2 最佳实线

2.1 导入操作

1 实体类说明

关于注解说明
easypoi起因就是Excel的导入导出,最初的模板是实体和Excel的对应,model–row,filed–col 这样利用注解我们可以和容易做到excel到导入导出
经过一段时间发展,现在注解有5个类分别是
@Excel 作用到filed上面,是对Excel一列的一个描述
@ExcelCollection 表示一个集合,主要针对一对多的导出,比如一个老师对应多个科目,科目就可以用集合表示
@ExcelEntity 表示一个继续深入导出的实体,但他没有太多的实际意义,只是告诉系统这个对象里面同样有导出的字段
@ExcelIgnore 和名字一样表示这个字段被忽略跳过这个导导出
@ExcelTarget 这个是作用于最外层的对象,描述这个对象的id,以便支持一个对象可以针对不同导出做出不同处理

package com.geekmice.springbootselfexercise.domain;import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelIgnore;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Date;/*** (User)实体类** @author pingmingbo* @since 2023-08-06 09:51:28*/
@Data
@ApiModel
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "user")
public class UserDomain implements Serializable {private static final long serialVersionUID = 723356122339609354L;/*** 编号*/@ApiModelProperty(value = "编号")@ExcelIgnore@TableId(type = IdType.AUTO)private Long id;/*** 用户名*/@ApiModelProperty(value = "用户名")@Excel(name = "用户名")@TableField(value = "user_name")private String userName;/*** 生日*/@ApiModelProperty(value = "生日")@Excel(name="生日")@TableField(value = "birthday")private Date birthday;/*** 性别*/@Excel(name = "性别")@ApiModelProperty(value = "性别")@TableField(value = "sex")private String sex;/*** 地址*/@ApiModelProperty(value = "地址")@Excel(name = "地址")@TableField(value = "address")private String address;}

2 业务层

public interface UserService extends IService<UserDomain> {void uploadFileByEasyPoi(MultipartFile file);
}package com.geekmice.springbootselfexercise.service.impl;import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.geekmice.springbootselfexercise.dao.UserDao;
import com.geekmice.springbootselfexercise.domain.UserDomain;
import com.geekmice.springbootselfexercise.service.UserService;
import com.sun.corba.se.impl.corba.ExceptionListImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** (User)表服务实现类** @author pingmingbo* @since 2023-08-06 09:51:28*/
@Service("userService")
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserDao, UserDomain> implements UserService {@Resourceprivate UserDao userDao;@Overridepublic void uploadFileByEasyPoi(MultipartFile file) {InputStream inputStream;try {inputStream = file.getInputStream();} catch (IOException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);}try {List<UserDomain> result = ExcelImportUtil.importExcel(inputStream, UserDomain.class, new ImportParams());for (UserDomain userDomain : result) {log.info("导入数据:【{}】", userDomain.toString());}this.saveBatch(result);log.info("录入结束");} catch (Exception e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);}}
}

3 效果

在这里插入图片描述

JDBC Connection [HikariProxyConnection@880330951 wrapping com.mysql.cj.jdbc.ConnectionImpl@55e295a5] will not be managed by Spring
==> Preparing: INSERT INTO user ( user_name, sex, address ) VALUES ( ?, ?, ? )
==> Parameters: pDwITO(String), 男(String), gqrkCeEpHh(String)
==> Parameters: TvOJNo(String), 男(String), BsAivNwjhP(String)
==> Parameters: miTVeL(String), 男(String), SMZOmxceID(String)
==> Parameters: siFTKf(String), 男(String), LcBfUNnITp(String)
==> Parameters: KGnyWX(String), 男(String), JXPbQboTdH(String)
==> Parameters: RyZYiE(String), 男(String), TydYPZxLah(String)
==> Parameters: JrcAnl(String), 男(String), bCttkDXCAN(String)
==> Parameters: nYMiAc(String), 男(String), uGXhBfyKgK(String)
==> Parameters: nDgzmK(String), 男(String), ZunMUZSYXa(String)
==> Parameters: xdLspA(String), 男(String), CLNrrpNHPw(String)
2023-08-06 14:00:51.218 INFO 9244 — [nio-8081-exec-3] c.g.s.service.impl.UserServiceImpl : 录入结束
在这里插入图片描述

3 控制层

    @PostMapping(value = "uploadFileByEasyPoi")@MethodExporter@ApiOperation(value = "通过easypoi上传文件")public AjaxResult uploadFileByEasyPoi(@RequestPart MultipartFile file){userService.uploadFileByEasyPoi(file);return AjaxResult.success();}

2.2 导出操作

核心代码

    @Overridepublic void downloadFileByEasyPoi() {List<UserDomain> list = new ArrayList(16);for (int i = 0; i < 10; i++) {UserDomain item = UserDomain.builder().userName(RandomStringUtils.randomAlphabetic(6)).address(RandomStringUtils.randomAlphabetic(10)).birthday(new Date()).sex("男").build();list.add(item);}Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), UserDomain.class, list);FileOutputStream outputStream;try {outputStream = new FileOutputStream("D:\\easypoi.xlsx");workbook.write(outputStream);} catch (FileNotFoundException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);} catch (IOException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);}}

2.3 大数据量处理

大数据导出是当我们的导出数量在几万,到上百万的数据时,一次从数据库查询这么多数据加载到内存然后写入会对我们的内存和CPU都产生压力,这个时候需要我们像分页一样处理导出分段写入Excel缓解Excel的压力 EasyPoi提供的是两个方法 *强制使用 xssf版本的Excel *

        <dependency><groupId>xerces</groupId><artifactId>xercesImpl</artifactId><version>2.9.1</version></dependency>

importExcelBySax方法

/*** Excel 通过SAX解析方法,适合大数据导入,不支持图片* 导入 数据源本地文件,不返回校验结果 导入 字 段类型 Integer,Long,Double,Date,String,Boolean* * @param inputstream* @param pojoClass* @param params* @param handler*/public static void importExcelBySax(InputStream inputstream, Class<?> pojoClass,ImportParams params, IReadHandler handler) {new SaxReadExcel().readExcel(inputstream, pojoClass, params, handler);}
    @Overridepublic void uploadBigFileByEasyPoi(MultipartFile file) {InputStream inputStream;try {inputStream = file.getInputStream();} catch (IOException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);}ExcelImportUtil.importExcelBySax(inputStream,UserDomain.class,new ImportParams(),new UserHandler<UserDomain>());}

10w条数据,35s
与正常导入,10w数据,20s

4 问题

4.1 日期类型字段特殊处理

format属性指定一下日期格式

    @Excel(name="生日",format = "yyyy-MM-dd")private Date birthday;

4.2 文件上传

修改默认上传大小

  servlet:multipart:max-file-size: 50MBmax-request-size: 50MB

4.3 指定导出路径

核心代码

FileOutputStream outputStream = new FileOutputStream("D:\\test.xlsx"); // 指定输出流位置   workbook.write(outputStream) // 写到workbook中

业务层

        List<UserDomain> list = new ArrayList(16);for (int i = 0; i < 100000; i++) {UserDomain item = UserDomain.builder().userName(RandomStringUtils.randomAlphabetic(6)).address(RandomStringUtils.randomAlphabetic(10)).birthday(new Date()).sex("男").build();list.add(item);}Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), UserDomain.class, list);FileOutputStream outputStream;try {outputStream = new FileOutputStream("D:\\easypoi.xlsx");workbook.write(outputStream);} catch (FileNotFoundException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);} catch (IOException e) {log.error("error msg 【{}】", e);throw new IllegalArgumentException(e);}log.info("easypoi导出结束");

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用乐观锁解决超卖问题

目录 什么是超卖&#xff1f; 乐观锁和悲观锁的定义 悲观锁&#xff1a; 乐观锁&#xff1a; 乐观锁的实现方式 1.版本号 2.CAS法 什么是超卖&#xff1f; 举个例子&#xff1a;订单系统中&#xff0c;用户在执行下单操作时&#xff0c;可能同一时间有无数个用户同时下单&…

Qt应用开发(基础篇)——框架类 QFrame

一、前言 QFrame继承于QWidget&#xff0c;被QLCDNumber、QToolBox、QLabel、QListView等部件继承&#xff0c;是一个拥有矩形框架的基类。 QFrame可以直接创建成一个没有内容的的矩形框架&#xff0c;框架的样式由边框厚度(lineWidth)、框架形状(QFrame::Shape)和阴影样式(QFr…

linux自定义网络访问规则

1.更改防火墙默认区域为trusted firewall-cmd --set-default-zonetrusted 2.新建一个zone&#xff0c;将想要访问本机80端口的ip&#xff0c;如&#xff1a;192.168.3.99 &#xff0c;添加的这个zone中&#xff0c;同时在这个zone中放行80端口。 firewall-cmd --permanent --ne…

Electron + Vue3 + Vite + TS 构建桌面应用

之前是使用React、Electron、TS和webpack来构建桌面应用的。虽然功能齐全,但是打包等等开发的体验不太理想,总感觉太慢了。作为一个开发者,我们总是希望,执行构建命令后,可以快速打包或者启动本地应用,且通过更少的配置,来完成开发体验。 现在的vite已经得到广泛的应用…

PHP流浪动物招领网站mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP流浪动物招领网站 是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 下载链接 nullhttps://download.csdn.net/download/qq_41221322/88190168视频演示 …

挖洞小技巧

挖洞小技巧 1. Google语法1.1. 基础语法1.2. 操作符 2. 寻找漏洞2.1. SQL注入2.1.1. 不带公司2.1.2. 带公司2.1.3. 如何测试 2.2. 后台管理漏洞2.2.1. 查询单个网站2.2.2. 常见后台管理路径 2.3. 支付漏洞2.4. 文件上传漏洞2.5. 查找文件类型2.6. 敏感信息泄露 3. 刷分 1. Goog…

opencv37-形态学操作-开运算(先腐蚀后膨胀)cv2.morphologyEx()-参数 op 设置为“cv2.MORPH_OPEN”

腐蚀操作和膨胀操作是形态学运算的基础&#xff0c;将腐蚀和膨胀操作进行组合&#xff0c;就可以实现开运算、闭运算&#xff08;关运算&#xff09;、形态学梯度&#xff08;MorphologicalGradient&#xff09;运算、礼帽运算&#xff08;顶帽运算&#xff09;、黑帽运算、击中…

ArcGIS Pro暨基础入门、制图、空间分析、影像分析、三维建模、空间统计分析与建模、python融合、案例应用

GIS是利用电子计算机及其外部设备&#xff0c;采集、存储、分析和描述整个或部分地球表面与空间信息系统。简单地讲&#xff0c;它是在一定的地域内&#xff0c;将地理空间信息和 一些与该地域地理信息相关的属性信息结合起来&#xff0c;达到对地理和属性信息的综合管理。GIS的…

Hadoop理论及实践-HDFS的Namenode及Datanode(参考Hadoop官网)

HDFS有什么特点&#xff0c;被设计做什么 Hadoop分布式文件系统(HDFS)被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统。有一下几个特点&#xff1a; HDFS是一个高度容错性的系统&#xff0c;具有高容错、高可靠性、高扩展性的特点&#xff0c;适合部…

复现原型链污染

目录 原型链污染是什么 例1 复现 例2 复现 原型链污染是什么 第一章中说到&#xff0c;foo.__proto__指向的是Foo类的prototype。那么&#xff0c;如果我们修改了foo.__proto__中的值&#xff0c;是不是就可以修改Foo类呢&#xff1f; 做个简单的实验&#xff1a; // fo…

Go语言进阶

个人笔记&#xff0c;大量摘自Go语言高级编程、Go|Dave Cheney等 更新 go get -u all 在非go目录运行go install golang.org/x/tools/goplslatest更新go tools&#xff1a;在go目录运行go get -u golang.org/x/tools/...&#xff0c;会更新bin目录下的应用&#xff1b; 运行…