ruoyi-Excel导入导出操作过程
本文介绍springboot中将前端展示的数据导出为EXCEL表格文件的方法,方法来自若依ruoyi
1.相关配置
1.1配置用注解
1.2添加字段
1.3添加注解
2.Excel方法类
导出方法路线图
2.1定义变量字段
2.2 Controller层
2.3 exportExcel(List list, String sheetName)方法
2.3.1 init方法(初始化)
2.3.2 调用creteExcelField()方法
2.3.3 createWorkbook()
2.4 exportExcel()
1:相关配置
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
将数据导出为EXCEL文件时有许许多多的配置,例如行高,日期格式,导出类型等,为实现代码的复用和功能的多样性,应使用注解的方法为不同的实体类,不同的数据类型进行单个配置。
package com.example.ruoyiexcel.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义导出Excel数据注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel {
}
@Rentention 用于定义该注解如何保持,即生命周期
@Target 用于描述注解的使用范围,被描述的注解可以用在什么地方
EXCEL表格所需要的配置例如:
package com.example.ruoyiexcel.common.annotation;
import com.example.ruoyiexcel.common.utils.poi.ExcelHandlerAdapter;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
/**
* 自定义导出Excel数据注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel {
/**
* 如果是外键,请设置类的路径值+方法名 (如: com.yz.service.impl.StudentServiceImpl+方法名)
*/
public String classTypeMethod() default "";
/**
* 设置为true. 配合 classTypeMethod 取接口中的数据,否则不应用接口中返回的数据
*/
public boolean useclassTypeMethodCombo() default true;
/**
* 是否必填列,如果必填列没有,则忽略此行
*/
public boolean required() default false;
/**
* 导出到Excel中的名字.
*/
public String name() default "";
/**
* 导出时在excel中排序
*/
public int sort() default Integer.MAX_VALUE;
/**
* 日期格式, 如: yyyy-MM-dd
*/
public String dateFormat() default "";
/**
* 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
*/
public String dictType() default "";
/**
* 读取内容转表达式 (如: 0=男,1=女,2=未知)
*/
public String readConverterExp() default "";
/**
* 分隔符,读取字符串组内容
*/
public String separator() default ",";
/**
* BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
*/
public int scale() default -1;
/**
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
*/
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/**
* 导出时在excel中每个列的高度 单位为字符
*/
public double height() default 14;
/**
* 导出时在excel中每个列的宽 单位为字符
*/
public double width() default 16;
/**
* 文字后缀,如% 90 变成90%
*/
public String suffix() default "";
/**
* 当值为空时,字段的默认值
*/
public String defaultValue() default "";
/**
* 提示信息
*/
public String prompt() default "";
/**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {};
/**
* 设置只能选择不能输入的列内容,设置为true. 配合 dictType 从字典表中取数据
*/
public boolean useDictCombo() default false;
/**
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
*/
public boolean isExport() default true;
/**
* 另一个类中的属性名称,支持多级获取,以小数点隔开
*/
public String targetAttr() default "";
/**
* 是否自动统计数据,在最后追加一行统计数据总和
*/
public boolean isStatistics() default false;
/**
* 导出类型(0数字 1字符串)
*/
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出列头背景色
*/
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
/**
* 导出列头字体颜色
*/
public IndexedColors headerColor() default IndexedColors.WHITE;
/**
* 导出单元格背景色
*/
public IndexedColors backgroundColor() default IndexedColors.WHITE;
/**
* 导出单元格字体颜色
*/
public IndexedColors color() default IndexedColors.BLACK;
/**
* 导出字段对齐方式
*/
public HorizontalAlignment align() default HorizontalAlignment.CENTER;
/**
* 自定义数据处理器
*/
public Class<?> handler() default ExcelHandlerAdapter.class;
/**
* 自定义数据处理器参数
*/
public String[] args() default {};
/**
* 字段类型(0:导出导入;1:仅导出;2:仅导入)
*/
Type type() default Type.ALL;
public enum Type {
ALL(0), EXPORT(1), IMPORT(2);
private final int value;
Type(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
public enum ColumnType {
NUMERIC(0), STRING(1), IMAGE(2);
private final int value;
ColumnType(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
}
具体配置根据自己需求来定,其中的枚举类型为后面判断使用方法所用。
具体使用步骤:例如
/** 订单编号 */
@Excel(name = "订单编号")
private String orderId;
/** 客户编号 */
@Excel(name = "客户编号")
private Long customerId;
/** 姓名 */
@Excel(name = "姓名")
private String Person;
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date Time;
2:ExcelUtil
2.1:表格的整体配置应在该类中添加
/**
* Excel相关处理
*
* @author ruoyi
*/
public class ExcelUtil<T>
{
private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
/**
* Excel sheet最大行数,默认65536
*/
public static final int sheetSize = 65536;
/**
* 工作表名称
*/
private String sheetName;
/**
* 导出类型(EXPORT:导出数据;IMPORT:导入模板)
*/
private Type type;
/**
* 工作薄对象
*/
private Workbook wb;
/**
* 工作表对象
*/
private Sheet sheet;
/**
* 样式列表
*/
private Map<String, CellStyle> styles;
/**
* 导入导出数据列表
*/
private List<T> list;
/**
* 注解列表
*/
private List<Object[]> fields;
/**
* 最大高度
*/
private short maxHeight;
/**
* 统计列表
*/
private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
/**
* 数字格式
*/
private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
/**
* 实体对象
*/
public Class<T> clazz;
public ExcelUtil(Class<T> clazz)
{
this.clazz = clazz;
}
工作簿和工作表的区别:
新建一个excel后系统默认一个工作簿,在这个簿中一般会友三张工作表,sheet1、sheet2、sheet3。就这么简单。一张工作簿中最多可容255张工作表,一张表最多有65536行。
2.2 :Controller层
在controller层需要先查询出数据,再初始化ExcelUtil并调用方法
List<Order> list=service.selectOrder(ids);
ExcelUtil<Order> util = new ExcelUtil<Order>(Order.class);
return util.exportExcel(list, "Excel数据表");
2.3 exportExcel(…)方法和importTemplateExcel方法
进入到exportExcel(List list, String sheetName)方法中
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param response 返回数据
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @return 结果
* @throws IOException
*/
public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) {
exportExcel(response, list, sheetName, StringUtils.EMPTY);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param response 返回数据
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @return 结果
* @throws IOException
*/
public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) {
exportExcel(response, list, sheetName, StringUtils.EMPTY);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param response 返回数据
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
* @throws IOException
*/
public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title) {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
this.init(list, sheetName, title, Type.EXPORT);
exportExcel(response);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @return 结果
*/
public void importTemplateExcel(HttpServletResponse response, String sheetName) {
importTemplateExcel(response, sheetName, StringUtils.EMPTY);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
*/
public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
this.init(null, sheetName, title, Type.IMPORT);
exportExcel(response);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @return 结果
*/
public void exportExcel(HttpServletResponse response) {
try {
writeSheet();
wb.write(response.getOutputStream());
} catch (Exception e) {
log.error("导出Excel异常{}", e.getMessage());
} finally {
IOUtils.closeQuietly(wb);
}
}
2.3.1 init方法(初始化)
public void init(List<T> list, String sheetName, String title, Type type) {
if (list == null) {
list = new ArrayList<T>();
}
this.list = list;
this.sheetName = sheetName;
this.type = type;
this.title = title;
createExcelField();
createWorkbook();
createTitle();
}
逻辑就是将需要的导出的数据和文件名,及区分导出还是导入的Type赋值给变量。
2.3.2 调用creteExcelField()方法
/**
* 得到所有定义字段
*/
private void createExcelField() {
this.fields = getFields();
this.childFields = getChildFields();
this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
this.maxHeight = getRowHeight();
}
/**
* 获取字段注解信息
*/
public List<Object[]> getFields() {
return getFields(clazz);
}
/**
* 获取字段注解信息
*/
public List<Object[]> getFields(Class<?> cla) {
List<Object[]> fields = new ArrayList<>();
List<Field> tempFields = new ArrayList<>();
tempFields.addAll(Arrays.asList(cla.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(cla.getDeclaredFields()));
for (Field field : tempFields) {
if (!ArrayUtils.contains(this.excludeFields, field.getName())) {
// 单注解
if (field.isAnnotationPresent(Excel.class)) {
Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {
field.setAccessible(true);
fields.add(new Object[]{field, attr});
}
}
// 多注解
if (field.isAnnotationPresent(Excels.class)) {
Excels attrs = field.getAnnotation(Excels.class);
Excel[] excels = attrs.value();
for (Excel attr : excels) {
if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {
field.setAccessible(true);
fields.add(new Object[]{field, attr});
}
}
}
}
}
return fields;
}
/**
* 获取字段注解信息
*/
public List<Object[]> getChildFields() {
return getChildFields(clazz);
}
/**
* 获取子表字段注解信息
*/
public List<Object[]> getChildFields(Class<?> cla) {
List<Object[]> fields = new ArrayList<>();
List<Field> tempFields = new ArrayList<>();
tempFields.addAll(Arrays.asList(cla.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(cla.getDeclaredFields()));
for (Field field : tempFields) {
if (!ArrayUtils.contains(this.excludeFields, field.getName())) {
if (field.isAnnotationPresent(ExcelChild.class)) {
ExcelChild attr = field.getAnnotation(ExcelChild.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {
field.setAccessible(true);
if (List.class.isAssignableFrom(field.getType())) {
ParameterizedType type = (ParameterizedType) field.getGenericType();
fields.add(new Object[]{field, attr, true, type.getActualTypeArguments()[0]});
} else {
fields.add(new Object[]{field, attr, false, field.getClass()});
}
}
}
}
}
return fields;
}
/**
* 根据注解获取最大行高
*/
public short getRowHeight() {
double maxHeight = 0;
for (Object[] os : this.fields) {
Excel excel = (Excel) os[1];
maxHeight = Math.max(maxHeight, excel.height());
}
return (short) (maxHeight * 20);
}
2.3.3 createWorkbook()
inti方法的最后一步,调用 createWorkbook()方法
/**
* 创建一个工作簿
*/
public void createWorkbook() {
this.wb = new SXSSFWorkbook(500);
this.sheet = wb.createSheet();
wb.setSheetName(0, sheetName);
this.styles = createStyles(wb);
}
/**
* 创建表格样式
*
* @param wb 工作薄对象
* @return 样式列表
*/
private Map<String, CellStyle> createStyles(Workbook wb) {
// 写入各条记录,每条记录对应excel表中的一行
Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
CellStyle style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
Font titleFont = wb.createFont();
titleFont.setFontName("Arial");
titleFont.setFontHeightInPoints((short) 16);
titleFont.setBold(true);
style.setFont(titleFont);
styles.put("title", style);
style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
Font dataFont = wb.createFont();
dataFont.setFontName("Arial");
dataFont.setFontHeightInPoints((short) 10);
style.setFont(dataFont);
styles.put("data", style);
style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
Font totalFont = wb.createFont();
totalFont.setFontName("Arial");
totalFont.setFontHeightInPoints((short) 10);
style.setFont(totalFont);
styles.put("total", style);
styles.putAll(annotationHeaderStyles(wb, styles));
styles.putAll(annotationDataStyles(wb));
return styles;
}
2.4 exportExcel()
调用完init方法,调用exportExcel无参方法
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @return 结果
*/
public void exportExcel(HttpServletResponse response) {
try {
writeSheet();
wb.write(response.getOutputStream());
} catch (Exception e) {
log.error("导出Excel异常{}", e.getMessage());
} finally {
IOUtils.closeQuietly(wb);
}
}
/**
* 创建写入数据到Sheet
*/
public void writeSheet() {
// 取出一共有多少个sheet.
int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
for (int index = 0; index < sheetNo; index++) {
createSheet(sheetNo, index);
// 产生一行
Row row = sheet.createRow(rownum);
int column = 0;
// 写入各个字段的列头名称
for (Object[] os : fields) {
Excel excel = (Excel) os[1];
this.createCell(excel, row, column++);
}
if (Type.EXPORT.equals(type)) {
fillExcelData(index, row);
addStatisticsRow();
}
}
}
/**
* 创建工作表
*
* @param sheetNo sheet数量
* @param index 序号
*/
public void createSheet(int sheetNo, int index) {
// 设置工作表的名称.
if (sheetNo > 1 && index > 0) {
this.sheet = wb.createSheet();
this.createTitle();
wb.setSheetName(index, sheetName + index);
}
}
/**
* 填充excel数据
*
* @param index 序号
* @param row 单元格行
*/
public void fillExcelData(int index, Row row) {
int startNo = index * sheetSize;
int endNo = Math.min(startNo + sheetSize, list.size());
for (int i = startNo; i < endNo; i++) {
row = sheet.createRow(i + 1 + rownum - startNo);
// 得到导出对象.
T vo = (T) list.get(i);
int column = 0;
for (Object[] os : fields) {
Field field = (Field) os[0];
Excel excel = (Excel) os[1];
this.addCell(excel, row, vo, field, column++);
}
}
}
/**
* 创建统计行
*/
public void addStatisticsRow() {
if (statistics.size() > 0) {
Row row = sheet.createRow(sheet.getLastRowNum() + 1);
Set<Integer> keys = statistics.keySet();
Cell cell = row.createCell(0);
cell.setCellStyle(styles.get("total"));
cell.setCellValue("合计");
for (Integer key : keys) {
cell = row.createCell(key);
cell.setCellStyle(styles.get("total"));
cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
}
statistics.clear();
}
}
/**
* 创建excel第一行标题
*/
public void createTitle() {
if (StringUtils.isNotEmpty(title)) {
Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
titleRow.setHeightInPoints(30);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellStyle(styles.get("title"));
titleCell.setCellValue(title);
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(),
this.fields.size() - 1));
}
}
/**
* 添加单元格
*/
public Cell addCell(Excel attr, Row row, T vo, Field field, int column) {
Cell cell = null;
try {
// 设置行高
row.setHeight(maxHeight);
// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
if (attr.isExport()) {
// 创建cell
cell = row.createCell(column);
cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
// 用于读取对象中的属性
Object value = getTargetValue(vo, field, attr);
String dateFormat = attr.dateFormat();
String readConverterExp = attr.readConverterExp();
String separator = attr.separator();
String dictType = attr.dictType();
String targetAttr = attr.targetAttr();
if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) {
cell.setCellValue(parseDateToStr(dateFormat, value));
} else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) {
cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));
} else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) {
cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator));
} else if (value instanceof BigDecimal && -1 != attr.scale()) {
cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());
} else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
cell.setCellValue(dataFormatHandlerAdapter(value, attr));
} else {
// 设置列类型
setCellVo(value, attr, cell);
}
addStatisticsData(column, Convert.toStr(value), attr);
}
} catch (Exception e) {
log.error("导出Excel失败{}", e);
}
return cell;
}