1、环境
springboot+mybatis+mysq
2、pom依赖导入
<!--easypoi-->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.1.0</version>
</dependency>
3、工具类
package com.cc.poi.controller;
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 cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
public class ExcelUtils {
/**
* excel 导出
*
* @param list 数据
* @param title 标题
* @param sheetName sheet名称
* @param pojoClass pojo类型
* @param fileName 文件名称
* @param isCreateHeader 是否创建表头
* @param response
*/
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) throws IOException {
ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
exportParams.setCreateHeadRows(isCreateHeader);
defaultExport(list, pojoClass, fileName, response, exportParams);
}
/**
* excel 导出
*
* @param list 数据
* @param title 标题
* @param sheetName sheet名称
* @param pojoClass pojo类型
* @param fileName 文件名称
* @param response
*/
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) throws IOException {
defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, ExcelType.XSSF));
}
/**
* excel 导出
*
* @param list 数据
* @param pojoClass pojo类型
* @param fileName 文件名称
* @param response
* @param exportParams 导出参数
*/
public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, ExportParams exportParams, HttpServletResponse response) throws IOException {
defaultExport(list, pojoClass, fileName, response, exportParams);
}
/**
* excel 导出
*
* @param list 数据
* @param fileName 文件名称
* @param response
*/
public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
defaultExport(list, fileName, response);
}
/**
* 默认的 excel 导出
*
* @param list 数据
* @param pojoClass pojo类型
* @param fileName 文件名称
* @param response
* @param exportParams 导出参数
*/
private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
downLoadExcel(fileName, response, workbook);
}
/**
* 默认的 excel 导出
*
* @param list 数据
* @param fileName 文件名称
* @param response
*/
private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
downLoadExcel(fileName, response, workbook);
}
/**
* 下载
*
* @param fileName 文件名称
* @param response
* @param workbook excel数据
*/
private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
try {
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + "." + ExcelTypeEnum.XLSX.getValue(), "UTF-8"));
workbook.write(response.getOutputStream());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* excel 导入
*
* @param filePath excel文件路径
* @param titleRows 标题行
* @param headerRows 表头行
* @param pojoClass pojo类型
* @param <T>
* @return
*/
public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
if (StringUtils.isBlank(filePath)) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headerRows);
params.setNeedSave(true);
params.setSaveUrl("/excel/");
try {
return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
} catch (NoSuchElementException e) {
throw new IOException("模板不能为空");
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* excel 导入
*
* @param file excel文件
* @param pojoClass pojo类型
* @param <T>
* @return
*/
public static <T> List<T> importExcel(MultipartFile file, Class<T> pojoClass) throws IOException {
return importExcel(file, 1, 1, pojoClass);
}
/**
* excel 导入
*
* @param file excel文件
* @param titleRows 标题行
* @param headerRows 表头行
* @param pojoClass pojo类型
* @param <T>
* @return
*/
public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
return importExcel(file, titleRows, headerRows, false, pojoClass);
}
/**
* excel 导入
*
* @param file 上传的文件
* @param titleRows 标题行
* @param headerRows 表头行
* @param needVerfiy 是否检验excel内容
* @param pojoClass pojo类型
* @param <T>
* @return
*/
public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, boolean needVerfiy, Class<T> pojoClass) throws IOException {
if (file == null) {
return null;
}
try {
return importExcel(file.getInputStream(), titleRows, headerRows, needVerfiy, pojoClass);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* excel 导入
*
* @param inputStream 文件输入流
* @param titleRows 标题行
* @param headerRows 表头行
* @param needVerfiy 是否检验excel内容
* @param pojoClass pojo类型
* @param <T>
* @return
*/
public static <T> List<T> importExcel(InputStream inputStream, Integer titleRows, Integer headerRows, boolean needVerfiy, Class<T> pojoClass) throws IOException {
if (inputStream == null) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headerRows);
params.setSaveUrl("/excel/");
params.setNeedSave(true);
params.setNeedVerify(needVerfiy);
try {
return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
} catch (NoSuchElementException e) {
throw new IOException("excel文件不能为空");
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* Excel 类型枚举
*/
enum ExcelTypeEnum {
XLS("xls"), XLSX("xlsx");
private String value;
ExcelTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
4、@Excel注解
这个是必须使用的注解,如果需求简单只使用这一个注解也是可以的,
涵盖了常用的Excel需求。其余注解可搜素官网进行了解
属性 | 类型 | 默认值 | 功能 |
---|---|---|---|
name | String | null | 列名,支持name_id |
needMerge | boolean | fasle | 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row) |
orderNum | String | "0" | 列的排序,支持name_id |
replace | String[] | {} | 值得替换 导出是{a_id,b_id} 导入反过来 |
savePath | String | "upload" | 导入文件保存路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/ |
type | int | 1 | 导出类型 1 是文本 2 是图片,3 是函数,10 是数字 默认是文本 |
width | double | 10 | 列宽 |
height | double | 10 | 列高,后期打算统一使用@ExcelTarget的height,这个会被废弃,注意 |
isStatistics | boolean | fasle | 自动统计数据,在追加一行统计,把所有数据都和输出 这个处理会吞没异常,请注意这一点 |
isHyperlink | boolean | false | 超链接,如果是需要实现接口返回对象 |
isImportField | boolean | true | 校验字段,看看这个字段是不是导入的Excel中有,如果没有说明是错误的Excel,读取失败,支持name_id |
exportFormat | String | "" | 导出的时间格式,以这个是否为空来判断是否需要格式化日期 |
importFormat | String | "" | 导入的时间格式,以这个是否为空来判断是否需要格式化日期 |
format | String | "" | 时间格式,相当于同时设置了exportFormat 和 importFormat |
databaseFormat | String | "yyyyMMddHHmmss" | 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式,用以转换时间格式输出 |
numFormat | String | "" | 数字格式化,参数是Pattern,使用的对象是DecimalFormat |
imageType | int | 1 | 导出类型 1 从file读取 2 是从数据库中读取 默认是文件 同样导入也是一样的 |
suffix | String | "" | 文字后缀,如% 90 变成90% |
isWrap | boolean | true | 是否换行 即支持\n |
mergeRely | int[] | {} | 合并单元格依赖关系,比如第二列合并是基于第一列 则{0}就可以了 |
mergeVertical | boolean | fasle | 纵向合并内容相同的单元格 |
fixedIndex | int | -1 | 对应excel的列,忽略名字 |
isColumnHidden | boolean | false | 导出隐藏列 |
5、conteroller、service及实体类
【1】conteroller
package com.cc.poi.controller;
import com.cc.poi.service.EasyPoiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author cc
* @data 2021年08月02日 22:58
*/
@RestController
@RequestMapping("/poi")
public class TestController {
@Autowired
private EasyPoiService easyPoiService;
/**
* 导出
*/
@RequestMapping(value = "/downLoadExcel", method = RequestMethod.GET)
@ResponseBody
public void downLoadExcel(HttpServletResponse response) {
try {
easyPoiService.downLoadExcel(response);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*导入
*/
@RequestMapping("/importXls")
@ResponseBody
public void importSS(@RequestParam("file") MultipartFile file) {
try {
easyPoiService.importXls(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
【2】service
package com.cc.poi.service.impl;
import com.cc.common.entity.CcIpRoster;
import com.cc.common.mapper.CcIpRosterMapper;
import com.cc.poi.controller.ExcelUtils;
import com.cc.poi.service.EasyPoiService;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author cc
* @data 2021年08月04日 0:00
*/
@Service
public class EasyPoiServiceImpl implements EasyPoiService {
@Autowired
private CcIpRosterMapper ccIpRosterMapper;
@Autowired
SqlSessionFactory sqlSessionFactory;
/**
* 导出
* @author cc
* @date 2021/8/6 23:19
* @param response
*/
public void downLoadExcel(HttpServletResponse response) throws IOException {
List<CcIpRoster> personList = ccIpRosterMapper.selectAll();
ExcelUtils.exportExcel(personList, null, "测试sheet", CcIpRoster.class, "测试信息表", response);
}
/**
* 导入
* @author cc
* @date 2021/8/6 23:19
* @param file
*/
public void importXls(MultipartFile file) throws IOException {
Date date = new Date();
List<CcIpRoster> personList = ExcelUtils.importExcel(file,0,1, CcIpRoster.class);
for (CcIpRoster ccIpRoster : personList) {
ccIpRoster.setIsDel(0);
ccIpRoster.setCreatedBy("cc");
ccIpRoster.setUpdatedBy("cc");
ccIpRoster.setCreatedTime(date);
ccIpRoster.setUpdatedTime(date);
}
long start = System.currentTimeMillis();
batchInsert(personList);
System.out.println("导入excel所花时间:" + (System.currentTimeMillis() - start)/1000 + "'s");
}
/**
* 批量插入
* @author cc
* @date 2021/8/6 23:19
* @param personList
*/
private void batchInsert(List<CcIpRoster> personList){
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try{
CcIpRosterMapper mapper = session.getMapper(CcIpRosterMapper.class);
List<CcIpRoster> list = new ArrayList<>();
for(int i=0 ; i< personList.size(); i++){
list.add(personList.get(i));
if(i%5000==0 || i==personList.size()-1){
mapper.batchInsert(list);
list.clear();
}
}
session.commit();
}catch (Exception e){
e.printStackTrace();
}finally {
session.close();
}
}
}
【3】mapper
<insert id="batchInsert">
insert into cc_ip_roster
(ip, is_del, status, created_by, created_time, updated_by, updated_time)
values
<foreach collection="list" item="eimt" index="index" separator=",">
(
#{eimt.ip,jdbcType=VARCHAR}, #{eimt.isDel,jdbcType=INTEGER},#{eimt.status,jdbcType=INTEGER},
#{eimt.createdBy,jdbcType=VARCHAR}, #{eimt.createdTime,jdbcType=TIMESTAMP},
#{eimt.updatedBy,jdbcType=VARCHAR}, #{eimt.updatedTime,jdbcType=TIMESTAMP}
)
</foreach>
</insert>
【4】实体
package com.cc.common.entity;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Id;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CcIpRoster {
@Id
private Long id;
@Excel(name = "IP", orderNum = "0", width = 15)
private String ip;
@Excel(name = "状态", orderNum = "1", width = 15)
private Integer status;
private Integer isDel;
private String createdBy;
private Date createdTime;
private String updatedBy;
private Date updatedTime;
}
6、批量保持说明
ExecutorType.BATCH。为Mybatis内置的一种ExecutorType。ExecutorType有3种,默认的是simple,该模式下它为每个语句的执行创建一个新的预处理语句,单条提交sql;而batch模式重复使用已经预处理的语句,并且批量执行所有更新语句,显然batch性能将更优; 但batch模式也有自己的问题,比如在Insert操作时,在事务没有提交之前,是没有办法获取到自增的id,这在某型情形下是不符合业务要求的
具体代码再service示例代码中,在本地测试10w条数据,插入时间为4秒
7、其他注意事项
【1】上传文件大小
springboot 默认文件上传大小有限制,当excel文件过大时,可能会上传失败.需进行配置
【2】上传的excel列头和对应字段的@Excel的name 属性保持一致。