SpringBoot-EasyPoi
http://doc.wupaas.com/docs/easypoi/easypoi-1c10lpehvgv0j 官方文档
Easypoi 介绍
在项目中,有时会出现需要将数据库数据导出报表等功能,这时一般会用到poi库。poi是一个专门给Java程序提供格式文档读写功能的API接口,包括各种微软的格式文档入excel、word等。
最常用的还是处理Excel格式导入导出(其他都不咋地)。
Easypoi是在poi接口基础上进行了封装,简化了操作,基于注解的方式。
Maven
SpringBoot的Maven
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.0.0</version>
</dependency
普通项目的Maven
<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>
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 | 导出隐藏列 |
下面我们创建2个用例:
package com.easypoi.pojo;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
/**
* 用户名
*/
@Excel(name = "用户名")
private String username;
/**
* 姓名
*/
@Excel(name = "姓名")
private String name;
/**
* 年龄
*/
@Excel(name = "年龄")
private Integer age;
/**
* 性别,数值0表示男,1表示女 自动替换
*/
@Excel(name = "性别",replace = {"男_0", "女_1"})
private String sex;
/**
* 籍贯
*/
@Excel(name = "籍贯")
private String address;
}
package com.easypoi.pojo;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Company {
@Excel(name = "公司名称",width =20)
private String name;
/**
* type为 2 表示字段类型为图片
* imageType为 1 表示从file读取
*/
@Excel(name = "公司logo",width =20,type = 2,imageType = 1)
private String logo;
@Excel(name = "公司介绍",width =100)
private String dec;
}
excel样式
package com.easypoi.utils;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.params.ExcelForEachParams;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import org.apache.poi.ss.usermodel.*;
/**
* @author 胡安民
* @date 2019/11/04
*/
public class ExcelStyleUtil implements IExcelExportStyler {
private static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT");
private static final short FONT_SIZE_TEN = 9; //字体大小
private static final short FONT_SIZE_ELEVEN = 12;//列大小
private static final short FONT_SIZE_TWELVE = 15;//大标题
/**
* 大标题样式
*/
private CellStyle headerStyle;
/**
* 每列标题样式
*/
private CellStyle titleStyle;
/**
* 数据行样式
*/
private CellStyle styles;
public ExcelStyleUtil(Workbook workbook) {
this.init(workbook);
}
/**
* 初始化样式
*
* @param workbook
*/
private void init(Workbook workbook) {
this.headerStyle = initHeaderStyle(workbook);
this.titleStyle = initTitleStyle(workbook);
this.styles = initStyles(workbook);
}
/**
* 大标题样式
*
* @param color
* @return
*/
@Override
public CellStyle getHeaderStyle(short color) {
return headerStyle;
}
/**
* 每列标题样式
*
* @param color
* @return
*/
@Override
public CellStyle getTitleStyle(short color) {
return titleStyle;
}
/**
* 数据行样式 (控制全部行的样式)
* @param parity 表示奇偶行
* @param entity 数据内容
* @return 样式
*/
@Override
public CellStyle getStyles(boolean parity, ExcelExportEntity entity) {
return styles;
}
/**
* 获取样式方法
*
* @param dataRow 数据行
* @param obj 对象
* @param data 数据
*/
@Override
public CellStyle getStyles(Cell cell, int dataRow, ExcelExportEntity entity, Object obj, Object data) {
return getStyles(true, entity);
}
/**
* 模板使用的样式设置
*/
@Override
public CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams) {
return null;
}
/**
* 初始化--大标题样式
*
* @param workbook
* @return
*/
private CellStyle initHeaderStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_TWELVE, true));
return style;
}
/**
* 初始化--每列标题样式
*
* @param workbook
* @return
*/
private CellStyle initTitleStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_ELEVEN, false));
//背景色
// style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); //灰色
// style.setFillForegroundColor(IndexedColors.AQUA.getIndex()); //浅蓝色
style.setFillForegroundColor(IndexedColors.SEA_GREEN.getIndex()); //海藻绿
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return style;
}
/**
* 初始化--数据行样式
*
* @param workbook
* @return
*/
private CellStyle initStyles(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_TEN, false));
style.setDataFormat(STRING_FORMAT);
return style;
}
/**
* 基础样式
*
* @return
*/
private CellStyle getBaseCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
//下边框
style.setBorderBottom(BorderStyle.THIN);
//左边框
style.setBorderLeft(BorderStyle.THIN);
//上边框
style.setBorderTop(BorderStyle.THIN);
//右边框
style.setBorderRight(BorderStyle.THIN);
//水平居中
style.setAlignment(HorizontalAlignment.CENTER);
//上下居中
style.setVerticalAlignment(VerticalAlignment.CENTER);
//设置自动换行
style.setWrapText(true);
return style;
}
/**
* 字体样式
*
* @param size 字体大小
* @param isBold 是否加粗
* @return
*/
private Font getFont(Workbook workbook, short size, boolean isBold) {
Font font = workbook.createFont();
//字体样式
font.setFontName("宋体");
//是否加粗
font.setBold(isBold);
//字体大小
font.setFontHeightInPoints(size);
return font;
}
}
excel倒入导出工具类
package com.easypoi.utils;
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.TemplateExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
/**
* Excel导入导出工具类
*/
public class ExcelUtils {
/** 允许导出的最大条数 */
private static final Integer EXPORT_EXCEL_MAX_NUM = 10000;
/**
* excel 导出
*
* @param list 数据列表
* @param fileName 导出时的excel名称
* @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 fileName 导出时的excel名称
* @param response
*/
private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
//把数据添加到excel表格中
Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
downLoadExcel(fileName, response, workbook);
}
/**
* excel 导出
*
* @param list 数据列表
* @param pojoClass pojo类型
* @param fileName 导出时的excel名称
* @param response
* @param exportParams 导出参数(标题、sheet名称、是否创建表头,表格类型)
*/
private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {
//把数据添加到excel表格中
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
downLoadExcel(fileName, response, workbook);
}
/**
* excel 导出
*
* @param list 数据列表
* @param pojoClass pojo类型
* @param fileName 导出时的excel名称
* @param exportParams 导出参数(标题、sheet名称、是否创建表头,表格类型)
* @param response
*/
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 title 表格内数据标题
* @param sheetName sheet名称
* @param pojoClass pojo类型
* @param fileName 导出时的excel名称
* @param response
*/
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) throws IOException {
//给文件名拼接上日期
SimpleDateFormat formatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");
String dateString = formatter.format (new Date());
fileName = fileName + dateString;
//判断导出数据是否为空
if (list == null) {
list = new ArrayList<>();
}
//判断导出数据数量是否超过限定值
if (list.size() > EXPORT_EXCEL_MAX_NUM) {
title = "导出数据行数超过:" + EXPORT_EXCEL_MAX_NUM + "条,无法导出、请添加导出条件!";
list = new ArrayList<>();
}
//获取导出参数
ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
//设置导出样式
exportParams.setStyle(ExcelStyleUtil.class);
//设置行高
exportParams.setHeight((short) 6);
defaultExport(list, pojoClass, fileName, response, exportParams);
}
/**
* 根据模板生成excel后导出
* @param templatePath 模板路径
* @param map 数据集合
* @param fileName 文件名
* @param response
* @throws IOException
*/
public static void exportExcel(TemplateExportParams templatePath, Map<String, Object> map, String fileName, HttpServletResponse response) throws IOException {
Workbook workbook = ExcelExportUtil.exportExcel(templatePath, map);
downLoadExcel(fileName, response, workbook);
}
/**
* excel下载
*
* @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 + ".xlsx", "UTF-8"));
workbook.setForceFormulaRecalculation(true); //强制开启excel公式计算
workbook.write(response.getOutputStream());
} 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 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 上传的文件
* @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 {
if (file == null) {
return null;
}
try {
return importExcel(file.getInputStream(), titleRows, headerRows, pojoClass);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* excel 导入
*
* @param inputStream 文件输入流
* @param titleRows 表格内数据标题行
* @param headerRows 表头行
* @param pojoClass pojo类型
* @param <T>
* @return
*/
public static <T> List<T> importExcel(InputStream inputStream, Integer titleRows, Integer headerRows, 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);
try {
return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
} catch (NoSuchElementException e) {
throw new IOException("excel文件不能为空");
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
测试接口
package com.easypoi.controller;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import com.easypoi.pojo.Company;
import com.easypoi.pojo.User;
import com.easypoi.utils.ExcelUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
@RestController
@RequestMapping("/easypoi")
public class EasypoiController {
/**
* 导入excel数据
* @param file
* @return
* @throws IOException
*/
@RequestMapping(value = "/import", method = RequestMethod.POST)
public ResponseEntity<String> importExcel(@RequestParam("file") MultipartFile file) throws IOException {
List<User> list = ExcelUtils.importExcel(file, User.class);
// int i = userService.insertByBatch(list); //将数据写入到数据库中
// if (i != 0) {
// return ResponseEntity.ok( "导入成功");
// } else {
// return ResponseEntity.ok( "导入失败");
// }
return ResponseEntity.ok( "导入成功");
}
/**
* 导出数据
* // http://localhost:1011/easypoi/exportExcel
* @param response
* @throws IOException
*/
@GetMapping("/exportExcel")
public void exportExcel( HttpServletResponse response) throws IOException {
List<User> list=new ArrayList<User>(){{
add(User.builder().username("我是大白鲨").age(12).name("胡安民").sex("0").address("北京市").build());
add(User.builder().username("我是大白鲨1").age(12).name("胡安民1").sex("1").address("北京市1").build());
add(User.builder().username("我是大白鲨1").age(12).name("胡安民1").sex("男").address("北京市1").build());
}};
ExcelUtils.exportExcel(list, "用户信息统计表Title", "用户表Sheet", User.class, "用户信息统计File", response);
}
/**
* excel带图片的导出
* // http://localhost:1011/easypoi/imgexport
* @param response
* @throws IOException
*/
@GetMapping("/imgexport")
public void imgExport(HttpServletResponse response) throws IOException {
List<Company> list = new ArrayList<>();
//图片的路径自定义,但必须要正确
list.add(new Company("百度", "E:/img/1.jpg", "百度一下你就知道"));
list.add(new Company("腾讯", "E:/img/3.jpg", "腾讯qq,交流的世界"));
list.add(new Company("阿里巴巴", "E:/img/2.jpg", "阿里巴巴,马云的骄傲"));
ExcelUtils.exportExcel(list, "图片excelTile", "图片excelSheet", Company.class, "图片excelFile", response);
}
/**
* excel带图片的导入 只支持在linux服务器上 (可以自行查看源码)
* // http://localhost:1011/easypoi/imgexport
* @param file
* @return
* @throws IOException
*/
@PostMapping("/imgimport")
public ResponseEntity imgImport(@RequestParam("file") MultipartFile file) throws IOException {
List<Company> list = ExcelUtils.importExcel(file, Company.class);
return ResponseEntity.ok("导入成功:"+list);
}
/**
* 使用模板excel导出 (专门处理一些复杂情况下的数据导出)
* //http://localhost:1011/easypoi/excelTemplate
* @param response
* @throws Exception
*/
@GetMapping("/excelTemplate")
public void makeExcelTemplate(HttpServletResponse response) throws Exception {
List<User> list=new ArrayList<User>(){{
add(User.builder().username("我是大白鲨").age(12).name("胡安民").sex("0").address("北京市").build());
add(User.builder().username("我是大白鲨1").age(12).name("胡安民1").sex("1").address("北京市1").build());
add(User.builder().username("我是大白鲨1").age(12).name("胡安民1").sex("男").address("北京市1").build());
}};
TemplateExportParams templatePath = new TemplateExportParams("E:/test.xlsx");
Map<String, Object> map = new HashMap<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
map.put("date", sdf.format(new Date()));
map.put("user", "admin");
map.put("userList", list);
ExcelUtils.exportExcel(templatePath, map, "使用模板excel导出File", response);
}
}
application.yml
server:
port: 1011
spring:
main:
allow-bean-definition-overriding: true #easypoi启用覆盖
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/voidme?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&serverTimezone=UTC
username: root
password: root
hikari.idle-timeout: 60000
hikari.maximum-pool-size: 30
hikari.minimum-idle: 10
#spring集成Mybatis环境
#实体类别名扫描包 如果使用接口的话这个可以省略
# 加载Mybatis映射文件 classpath:mapper/*Mapper.xml 代表是 resources 下mapper包里所有是 ..Mapper.xml结尾的xml配置文件 如果使用接口的话这个可以省略
mybatis:
type-aliases-package: com.easypoi.pojo
mapper-locations: classpath:mybaitis/*Mapper.xml
configuration:
map-underscore-to-camel-case: true
启动类
package com.easypoi;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class EasypoiAppllication {
public static void main(String[] args) {
SpringApplication.run(EasypoiAppllication.class,args);
}
}
效果图
导出效果图
带图片的导出效果图
使用模板excel导出效果图
使用模板excel导出,@Excel注解(无效) 这个是BUG占时官方没有解决
excel模板导出规则
在上面我们已经提供了接口了, 但是excel的模板格式 和规则是什么呢???
在两个大括号里写对应的数据名称。
$fe用来遍历数据 , : list数据源 (变量默认t,不需要写)
{{$fe: maplist t.id }}
http://doc.wupaas.com/docs/easypoi/easypoi-1c10lfhut694k (官方,一般不建议使用代码操作太复杂的表格了)