EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。快速、简洁、解决大文件内存溢出的java处理Excel工具
- 快速:快速的读取
excel
中的数据。 - 简洁:映射
excel
和实体类,让代码变的更加简洁。 - 大文件:在读写大文件的时候使用磁盘做缓存,更加的节约内存。
当项目中同时使用easypoi和easyExcel时,easypoi依赖放在easyExcel的前面
导出常用注解
注解 | 类型 | 描述 |
---|---|---|
ExcelProperty | 导出 | index 指定写到第几列,默认根据成员变量排序。value 指定写入的名称,默认成员变量的名字,多个value 可以参照快速开始中的复杂头 |
ExcelIgnore | 导出 | 默认所有字段都会写入excel,这个注解会忽略这个字段 |
DateTimeFormat | 导出 | 日期转换,将Date 写到excel会调用这个注解。里面的value 参照java.text.SimpleDateFormat |
NumberFormat | 导出 | 数字转换,用Number 写excel会调用这个注解。里面的value 参照java.text.DecimalFormat |
ExcelIgnoreUnannotated | 导出 | 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与 |
导出方法参数:WriteWorkbook
,WriteSheet
,WriteTable
都会有的参数,如果为空,默认使用上级。
converter
转换器,默认加载了很多转换器。也可以自定义。writeHandler
写的处理器。可以实现WorkbookWriteHandler
,SheetWriteHandler
,RowWriteHandler
,CellWriteHandler
,在写入excel的不同阶段会调用relativeHeadRowIndex
距离多少行后开始。也就是开头空几行needHead
是否导出头head
与clazz
二选一。写入文件的头列表,建议使用class。clazz
与head
二选一。写入文件的头对应的class,也可以使用注解。autoTrim
字符串、表头等数据自动trim
WriteWorkbook(理解成excel对象)参数
excelType
当前excel的类型 默认xlsx
outputStream
与file
二选一。写入文件的流file
与outputStream
二选一。写入的文件templateInputStream
模板的文件流templateFile
模板文件autoCloseStream
自动关闭流。password
写的时候是否需要使用密码useDefaultStyle
写的时候是否是使用默认头
WriteSheet(就是excel的一个Sheet)参数
sheetNo
需要写入的编码。默认0- sheetName
需要些的Sheet名称,默认同
sheetNo
WriteTable(就把excel的一个Sheet,一块区域看一个table)参数
tableNo
需要写入的编码。默认0
样式注解
- @ExcelProperty
- @ColumnWith 列宽
- @ContentFontStyle 文本字体样式
- @ContentLoopMerge 文本合并
- @ContentRowHeight 文本行高度
- @ContentStyle 文本样式
- @HeadFontStyle 标题字体样式
- @HeadRowHeight 标题高度
- @HeadStyle 标题样式
- @ExcelIgnore 忽略项
- @ExcelIgnoreUnannotated 忽略未注解
@ExcelProperty
必要的一个注解,注解中有三个参数value,index分别代表列明,列序号value和index只能二选一,通常不用设置converter
- value 通过标题文本对应
- index 通过文本行号对应
文件后缀
文件后缀 | MIME TYPE |
---|---|
.doc | application/msword |
.dot | application/msword |
.docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
.dotx | application/vnd.openxmlformats-officedocument.wordprocessingml.template |
.docm | application/vnd.ms-word.document.macroEnabled.12 |
.dotm | application/vnd.ms-word.template.macroEnabled.12 |
.xls | application/vnd.ms-excel |
.xlt | application/vnd.ms-excel |
.xla | application/vnd.ms-excel |
.xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
.xltx | application/vnd.openxmlformats-officedocument.spreadsheetml.template |
.xlsm | application/vnd.ms-excel.sheet.macroEnabled.12 |
.xltm | application/vnd.ms-excel.template.macroEnabled.12 |
.xlam | application/vnd.ms-excel.addin.macroEnabled.12 |
.xlsb | application/vnd.ms-excel.sheet.binary.macroEnabled.12 |
application/pdf | |
.ppt | application/vnd.ms-powerpoint |
.pot | application/vnd.ms-powerpoint |
.pps | application/vnd.ms-powerpoint |
.ppa | application/vnd.ms-powerpoint |
.pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
.potx | application/vnd.openxmlformats-officedocument.presentationml.template |
.ppsx | application/vnd.openxmlformats-officedocument.presentationml.slideshow |
.ppam | application/vnd.ms-powerpoint.addin.macroEnabled.12 |
.pptm | application/vnd.ms-powerpoint.presentation.macroEnabled.12 |
.potm | application/vnd.ms-powerpoint.presentation.macroEnabled.12 |
.ppsm | application/vnd.ms-powerpoint.slideshow.macroEnabled.12 |
.zip | application/zip |
.tar | application/x-tar |
.gif | image/gif |
.png | image/png |
.html | text/html |
.txt | text/plain |
添加pom依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
<!--工具类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.21</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
第一种:普通导出
实体类
package com.example.mybatismysql8demo.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class GoodsExportExcel {
@ExcelProperty(value = {"商品信息","商品名称"},index = 0)
public String goodsName;
@ExcelProperty(value = {"商品信息","商品价格"},index = 1)
public BigDecimal price;
@ExcelProperty(value = {"商品信息","商品数量"},index = 2)
public Integer num;
public GoodsExportExcel(String goodsName, BigDecimal price, Integer num) {
this.goodsName = goodsName;
this.price = price;
this.num = num;
}
}
执行方法
package com.example.mybatismysql8demo.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.example.mybatismysql8demo.excel.GoodsExportExcel;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
@RestController
public class EasyExcelController {
@RequestMapping(value = "easyExcelExport", method = RequestMethod.GET)
public void easyExcelExport(HttpServletResponse response) {
//数据
List<GoodsExportExcel> data = new ArrayList<>();
data.add(new GoodsExportExcel("苹果",new BigDecimal(10),100));
data.add(new GoodsExportExcel("香蕉",new BigDecimal(8),200));
try {
//设置返回类型及编码类型
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
//可以防止中文乱码,和easyExcel没有关系
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(System.currentTimeMillis() + ".xls" , StandardCharsets.UTF_8));
//浏览器下载
EasyExcel.write(response.getOutputStream(), GoodsExportExcel.class)
//文件类型
.excelType(ExcelTypeEnum.XLS)
//是否自动关闭流
.autoCloseStream(Boolean.TRUE)
//自动列宽(不太精确)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.sheet("商品信息")
.doWrite(data);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 本地下载
* @param args
*/
public static void main(String[] args) {
// 存放路径
String path = "D:/";
// 文件名称
String fileName = path + System.currentTimeMillis() + ".xls";
//数据
List<GoodsExportExcel> data = new ArrayList<>();
data.add(new GoodsExportExcel("苹果",new BigDecimal(10),100));
data.add(new GoodsExportExcel("香蕉",new BigDecimal(8),200));
EasyExcel.write(fileName, GoodsExportExcel.class).sheet("商品信息").doWrite(data);
}
}
第二种:复杂导出(合并、批注、自定义拦截、一级下拉框)
实体类
package com.example.mybatismysql8demo.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class GoodsExportExcel {
@ExcelProperty(value = {"商品信息","商品名称"},index = 0)
public String goodsName;
@ExcelProperty(value = {"商品信息","商品价格"},index = 1)
public BigDecimal price;
@ExcelProperty(value = {"商品信息","商品数量"},index = 2)
public Integer num;
@ExcelProperty(value = {"商品信息","是否进口"},index = 3)
public String inward;
public GoodsExportExcel(String goodsName, BigDecimal price, Integer num) {
this.goodsName = goodsName;
this.price = price;
this.num = num;
}
}
合并规则
package com.example.mybatismysql8demo.handler;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import java.util.List;
import java.util.Map;
/**
* excel导出数据内容单元格合并规则
* @author Administrator
*/
@Slf4j
public class ExcelFileCellMergeStrategy implements CellWriteHandler, RowWriteHandler, SheetWriteHandler {
/**
* 合并列的范围索引
*/
private int[] mergeColumnIndex;
/**
* 合并起始行索引
*/
private int mergeRowIndex;
/**
* 合并校验列值
*/
private int colIndex;
/**下拉框数据*/
private Map<Integer, String[]> mapDropDown;
public ExcelFileCellMergeStrategy(int[] mergeColumnIndex, int mergeRowIndex, int colIndex, Map<Integer, String[]> mapDropDown) {
this.mergeColumnIndex = mergeColumnIndex;
this.mergeRowIndex = mergeRowIndex;
this.colIndex = colIndex;
this.mapDropDown = mapDropDown;
}
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {
}
@Override
public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
System.out.println("----------合并开始-----------");
//当前行
int curRowIndex = cell.getRowIndex();
//当前列
int curColIndex = cell.getColumnIndex();
//某行开始合并
if (curRowIndex > mergeRowIndex) {
//判断每行的第一列是否相同,相同时进行合并
if (isSame(cell, curRowIndex)) {
for (int columnIndex : mergeColumnIndex) {
//对应的列进行合并
if (curColIndex == columnIndex) {
mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
break;
}
}
}
}
System.out.println("----------合并结束-----------");
System.out.println("----------自定义拦截开始-----------");
// 这里可以对cell进行任何操作
log.info("第{}行,第{}列写入完成", cell.getRowIndex(), cell.getColumnIndex());
if (aBoolean && cell.getRowIndex() == 1 && cell.getColumnIndex() == 0) {
CreationHelper createHelper = writeSheetHolder.getSheet().getWorkbook().getCreationHelper();
Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL);
hyperlink.setAddress("https://github.com/alibaba/easyexcel");
cell.setHyperlink(hyperlink);
}
System.out.println("----------自定义拦截结束-----------");
}
private Boolean isSame(Cell cell, int curRowIndex){
//获取当前行的某列的数据
Cell curCell = cell.getSheet().getRow(curRowIndex).getCell(colIndex);
//当前行某列值
Object curData = curCell.getCellTypeEnum() == CellType.STRING ? curCell.getStringCellValue() : curCell.getNumericCellValue();
//获取当前行的上一行数据
Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(colIndex);
//上一行某列值
Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
// 比较当前行的某列的单元格与上一行是否相同,相同合并当前单元格与上一行
return curData.equals(preData);
}
/**
* 当前单元格向上合并
* @param writeSheetHolder
* @param cell
* @param curRowIndex
* @param curColIndex
*/
private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
//获取当前行的当前列的数据和上一行的当前列数据,通过上一行数据是否相同进行合并
Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
Cell preCell = cell.getSheet().getRow(curRowIndex - 1 ).getCell(curColIndex);
Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
//比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行
if (curData.equals(preData)){
Sheet sheet = writeSheetHolder.getSheet();
List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
boolean isMerged = false;
for (int i = 0; i < mergedRegions.size() && !isMerged; i++) {
CellRangeAddress cellAddresses = mergedRegions.get(i);
//若上 一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
if (cellAddresses.isInRange(curRowIndex - 1 , curColIndex)){
sheet.removeMergedRegion(i);
cellAddresses.setLastRow(curRowIndex);
sheet.addMergedRegion(cellAddresses);
isMerged = true;
}
}
//若上一个单元格未被合并,则新增合并单元
if (!isMerged){
CellRangeAddress cellAddresses = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
sheet.addMergedRegion(cellAddresses);
}
}
}
@Override
public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer integer, Integer integer1, Boolean aBoolean) {
}
@Override
public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {
}
@Override
public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {
//integer表示行数,给第二行添加批注
if (BooleanUtils.isTrue(aBoolean) && integer == 1) {
Sheet sheet = writeSheetHolder.getSheet();
Drawing<?> drawingPatriarch = sheet.createDrawingPatriarch();
ClientAnchor anchor = new HSSFClientAnchor();
anchor.setCol1(1);
anchor.setCol2(2);
anchor.setRow1(0);
anchor.setRow2(2);
anchor.setDx1(0);
anchor.setDy1(0);
anchor.setDx2(0);
anchor.setDy2(0);
// 在第二行的第二列创建一个批注
Comment comment = drawingPatriarch.createCellComment(anchor);
// 输入批注信息
comment.setString(new HSSFRichTextString("创建批注!"));
// 将批注添加到第一行第一列单元格对象中
sheet.getRow(0).getCell(0).setCellComment(comment);
}
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
System.out.println("----下拉框---");
Sheet sheet = writeSheetHolder.getSheet();
///开始设置下拉框
DataValidationHelper helper = sheet.getDataValidationHelper();
for (Map.Entry<Integer, String[]> entry : mapDropDown.entrySet()) {
/*起始行、终止行、起始列、终止列**/
CellRangeAddressList addressList = new CellRangeAddressList(2, 1000, entry.getKey(), entry.getKey());
/*设置下拉框数据**/
DataValidationConstraint constraint = helper.createExplicitListConstraint(entry.getValue());
DataValidation dataValidation = helper.createValidation(constraint, addressList);
sheet.addValidationData(dataValidation);
}
System.out.println("----下拉框结束---");
}
}
执行方法
package com.example.mybatismysql8demo.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.example.mybatismysql8demo.excel.GoodsExportExcel;
import com.example.mybatismysql8demo.handler.ExcelFileCellMergeStrategy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
@RestController
public class EasyExcelController {
@RequestMapping(value = "easyExcelExport", method = RequestMethod.GET)
public void easyExcelExport(HttpServletResponse response) {
//数据
List<GoodsExportExcel> data = new ArrayList<>();
data.add(new GoodsExportExcel("苹果",new BigDecimal(10),100));
data.add(new GoodsExportExcel("苹果",new BigDecimal(9),50));
data.add(new GoodsExportExcel("香蕉",new BigDecimal(8),200));
try {
//设置返回类型及编码类型
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
//可以防止中文乱码,和easyExcel没有关系
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(System.currentTimeMillis() + ".xls" , StandardCharsets.UTF_8));
//需要合并的索引列(列从0开始)
int[] mergeColIndex = {0};
//需要从第二行开始,列头第一行
int mergeRowIndex = 2;
//设置当某列值相同才合并(列从0开始)
int colIndex = 0;
//下拉框值设置(实际运用在模版下载)
String[] yesOrNo = new String[]{"是", "否"};
Map<Integer, String[]> mapDropDown = new HashMap<>(1);
//3表示实体类中的index值对应
mapDropDown.put(3, yesOrNo);
//浏览器下载
EasyExcel.write(response.getOutputStream(), GoodsExportExcel.class)
//文件类型
.excelType(ExcelTypeEnum.XLS)
//是否自动关闭流
.autoCloseStream(Boolean.TRUE)
//设置批注
.inMemory(Boolean.TRUE)
//合并策略
.registerWriteHandler(new ExcelFileCellMergeStrategy(mergeColIndex, mergeRowIndex,colIndex,mapDropDown))
//自动列宽(不太精确)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.sheet("商品信息")
.doWrite(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
第三种:样式导出
- 忽视无注解的字段:@ExcelIgnoreUnannotated
- 表头行高:@HeadRowHeight(35)
- 数据列宽:@ColumnWidth(20)
- 数据行高:@ContentRowHeight(30)
- 头背景设置成红色 IndexedColors.RED.getIndex():@HeadStyle(fillPatternType =
FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 10) - 头字体设置成20:@HeadFontStyle(fontHeightInPoints = 20)
- 内容的背景设置成绿色 IndexedColors.GREEN.getIndex():@ContentStyle(fillPatternType =
FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 17) - 内容字体设置成20:@ContentFontStyle(fontHeightInPoints = 20)
- 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex():@HeadStyle(fillPatternType =
FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 14) - 字符串的头字体设置成20:@HeadFontStyle(fontHeightInPoints = 30)
- 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex():@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 40)
- 字符串的内容字体设置成20:@ContentFontStyle(fontHeightInPoints = 30)
实体类
package com.example.mybatismysql8demo.excel;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.*;
import lombok.Data;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import java.math.BigDecimal;
@Data
//忽视无注解的字段
@ExcelIgnoreUnannotated
/*表头行高*/
@HeadRowHeight(35)
/*数据列宽*/
@ColumnWidth(20)
/*数据行高*/
@ContentRowHeight(30)
// 头字体设置成20
@HeadFontStyle(fontHeightInPoints = 20)
// 头背景设置成黄色 IndexedColors.YELLOW.getIndex()
@HeadStyle(fillForegroundColor = 27, horizontalAlignment= HorizontalAlignment.CENTER)
// 内容的背景设置成粉色 IndexedColors.PINK.getIndex()
@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND,fillForegroundColor = 14, horizontalAlignment= HorizontalAlignment.CENTER,borderBottom = BorderStyle.THIN,borderLeft = BorderStyle.THIN)
// 内容字体设置成16
@ContentFontStyle(fontHeightInPoints = 16)
public class GoodsExportExcel {
@ExcelProperty(value = {"商品信息","商品名称"},index = 0)
public String goodsName;
@ExcelProperty(value = {"商品信息","商品价格"},index = 1)
public BigDecimal price;
@ExcelProperty(value = {"商品信息","商品数量"},index = 2)
public Integer num;
@ExcelProperty(value = {"商品信息","是否进口"},index = 3)
public String inward;
/**列标题样式*/
@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 10)
/**字体样式*/
@ContentFontStyle(fontHeightInPoints = 11,color = 10)
@ColumnWidth(30)
@ExcelProperty(value = {"商品信息","错误信息"},index = 4)
private String errorMsg;
private String ignore;
public GoodsExportExcel(String goodsName, BigDecimal price, Integer num) {
this.goodsName = goodsName;
this.price = price;
this.num = num;
}
}
执行方法
package com.example.mybatismysql8demo.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.example.mybatismysql8demo.excel.GoodsExportExcel;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
@RestController
public class EasyExcelController {
@RequestMapping(value = "easyExcelExport", method = RequestMethod.GET)
public void easyExcelExport(HttpServletResponse response) {
//数据
List<GoodsExportExcel> data = new ArrayList<>();
data.add(new GoodsExportExcel("苹果",new BigDecimal(10),100));
data.add(new GoodsExportExcel("苹果",new BigDecimal(9),50));
data.add(new GoodsExportExcel("香蕉",new BigDecimal(8),200));
try {
//设置返回类型及编码类型
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
//可以防止中文乱码,和easyExcel没有关系
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(System.currentTimeMillis() + ".xls" , StandardCharsets.UTF_8));
//表头样式
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
//设置表头居中对齐
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
//内容样式
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//设置内容靠左对齐
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
//浏览器下载
EasyExcel.write(response.getOutputStream(), GoodsExportExcel.class)
//文件类型
.excelType(ExcelTypeEnum.XLS)
//是否自动关闭流
.autoCloseStream(Boolean.TRUE)
//自动列宽(不太精确)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
//设置样式
.registerWriteHandler(new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle))
.sheet("商品信息")
.doWrite(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
第四种:多级下拉框导出
实体类
package com.example.mybatismysql8demo.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Data
public class CityInfo {
@ExcelProperty(value = "名称",index = 0)
private String name;
@ExcelProperty(value = "省",index = 1)
private String province;
@ExcelProperty(value = "市",index = 2)
private String city;
@ExcelProperty(value = "区",index = 3)
private String area;
}
多级下拉框配置
package com.example.mybatismysql8demo.config;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFDataValidationHelper;
import java.util.List;
import java.util.Map;
public class SpinnerWriteHandler implements SheetWriteHandler {
/**
* 多级分类值
*/
private Map<Integer, Object> dropDown;
/**
* 一级分类值
*/
private Map<Integer, String[]> oneDropDown;
/**
* 一级分类名称
*/
private List<String> firstLevel;
/**
* 父级分类名称:一级与二级分类名称
*/
private List<String> secondLevel;
/**
* 父级对应的子类
*/
private Map<String, List<String>> thirdLevel;
/**
* 开始行填充下拉框值
*/
private Integer rowIndex;
public SpinnerWriteHandler(Map<Integer, Object> dropDown, Map<Integer, String[]> oneDropDown,Integer rowIndex) {
this.dropDown = dropDown;
this.oneDropDown = oneDropDown;
this.rowIndex = rowIndex;
}
public SpinnerWriteHandler(Map<Integer, Object> dropDown,Integer rowIndex) {
this.dropDown = dropDown;
this.rowIndex = rowIndex;
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
//多级下拉框
if (dropDown != null) {
Sheet sheet = writeSheetHolder.getSheet();
///开始设置下拉框
DataValidationHelper helper = sheet.getDataValidationHelper();
//设置数据有效性和添加名称管理器
Workbook workbook = writeWorkbookHolder.getWorkbook();
for (Map.Entry<Integer, Object> entry : dropDown.entrySet()) {
//多级分类
Map<String, Object> values = (Map<String, Object>) entry.getValue();
firstLevel = (List<String>) values.get("firstLevel");
secondLevel = (List<String>) values.get("secondLevel");
thirdLevel = (Map<String, List<String>>) values.get("thirdLevel");
twoDownList(workbook, helper, sheet, entry.getKey());
}
}
//一级分类
if (oneDropDown != null) {
oneDownList(writeSheetHolder);
}
}
public void twoDownList(Workbook workbook, DataValidationHelper helper, Sheet sheet, Integer index) {
Sheet hideSheet = workbook.createSheet("category");
int rowId = 0;
//一级数据填充
Row provinceRow = hideSheet.createRow(rowId++);
provinceRow.createCell(0).setCellValue("省列表");
for (int i = 0; i < firstLevel.size(); i++) {
//设置列
Cell provinceCell = provinceRow.createCell(i + 1);
//列对应的值
provinceCell.setCellValue(firstLevel.get(i));
}
// 二级数据填充,将具体的数据写入到每一行中,行开头为父级区域,后面是子区域。二级与三级分类
for (String key : secondLevel) {
List<String> son = thirdLevel.get(key);
//判空
if (CollectionUtil.isNotEmpty(son)){
Row row = hideSheet.createRow(rowId++);
row.createCell(0).setCellValue(key);
for (int j = 0; j < son.size(); j++) {
Cell cell = row.createCell(j + 1);
cell.setCellValue(son.get(j));
}
// 添加名称管理器
String range = getRange(1, rowId, son.size());
Name name = workbook.createName();
//key不可重复
//判断key是否包含特殊符号
name.setNameName(key);
String formula = "category!" + range;
name.setRefersToFormula(formula);
}
}
//起始行、终止行、起始列、终止列
CellRangeAddressList addressList = new CellRangeAddressList(rowIndex, 1000, index, index);
//设置下拉框数据
DataValidationConstraint constraint = helper.createExplicitListConstraint(firstLevel.toArray(new String[]{}));
DataValidation dataValidation = helper.createValidation(constraint, addressList);
//处理Excel兼容性问题
if (dataValidation instanceof XSSFDataValidation) {
dataValidation.setSuppressDropDownArrow(true);
dataValidation.setShowErrorBox(true);
} else {
dataValidation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(dataValidation);
//对1000行设置有效性:i表示从第几行开始下标为1开始计算
for (int i = rowIndex; i < Integer.sum(1000,rowIndex); i++) {
//offset表示一级关联对应的坐标值,colNum表示当前级的子级列值,下标从1开始算
//setDataValidation("C" ,sheetPro,i,4);
setDataValidation(indexToColumn(index + 1), (SXSSFSheet) sheet, i, index + 2);
//offset表示二级关联对应的坐标值,colNum表示当前级的子级列值,下标从1开始算
//setDataValidation("D" ,sheetPro,i,5);
setDataValidation(indexToColumn(index + 2), (SXSSFSheet) sheet, i, index + 3);
}
}
/**
* 将excel表格中列索引转成列号字母,从A对应1开始
*/
private static String indexToColumn(int index) {
if (index <= 0) {
try {
throw new Exception("Invalid parameter");
} catch (Exception e) {
e.printStackTrace();
}
}
index--;
StringBuilder column = new StringBuilder();
do {
if (column.length() > 0) {
index--;
}
column.insert(0, ((char) (index % 26 + (int) 'A')));
index = (int) ((index - index % 26) / 26);
} while (index > 0);
return column.toString();
}
//一级下拉框
public void oneDownList(WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
///开始设置下拉框
DataValidationHelper helper = sheet.getDataValidationHelper();
for (Map.Entry<Integer, String[]> entry : oneDropDown.entrySet()) {
/*起始行、终止行、起始列、终止列**/
CellRangeAddressList addressList = new CellRangeAddressList(rowIndex, 1000, entry.getKey(), entry.getKey());
/*设置下拉框数据**/
DataValidationConstraint constraint = helper.createExplicitListConstraint(entry.getValue());
DataValidation dataValidation = helper.createValidation(constraint, addressList);
/*处理Excel兼容性问题**/
if (dataValidation instanceof XSSFDataValidation) {
dataValidation.setSuppressDropDownArrow(true);
dataValidation.setShowErrorBox(true);
} else {
dataValidation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(dataValidation);
}
}
/**
* 设置有效性
*
* @param offset 主影响单元格所在列,即此单元格由哪个单元格影响联动
* @param sheet
* @param rowNum 行数
* @param colNum 列数
*/
public static void setDataValidation(String offset, SXSSFSheet sheet, int rowNum, int colNum) {
DataValidationHelper helper = sheet.getDataValidationHelper();
DataValidation dataValidationList;
dataValidationList = getDataValidationByFormula("INDIRECT($" + offset + (rowNum) + ")", rowNum, colNum, (XSSFDataValidationHelper) helper);
sheet.addValidationData(dataValidationList);
}
/**
* 加载下拉列表内容
*
* @param formulaString
* @param naturalRowIndex
* @param naturalColumnIndex
* @param dvHelper
* @return
*/
private static DataValidation getDataValidationByFormula(String formulaString, int naturalRowIndex, int naturalColumnIndex, XSSFDataValidationHelper dvHelper) {
/* 加载下拉列表内容
* 举例:若formulaString = "INDIRECT($A$2)" 表示规则数据会从名称管理器中获取key与单元格 A2 值相同的数据,
* 如果A2是江苏省,那么此处就是江苏省下的市信息。
*/
DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint(formulaString);
// 设置数据有效性加载在哪个单元格上。四个参数分别是:起始行、终止行、起始列、终止列
int firstRow = naturalRowIndex - 1;
int lastRow = naturalRowIndex - 1;
int firstCol = naturalColumnIndex - 1;
int lastCol = naturalColumnIndex - 1;
CellRangeAddressList regions = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);
// 数据有效性对象
XSSFDataValidation dataValidationList = (XSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);
dataValidationList.setEmptyCellAllowed(false);
// 设置输入信息提示信息
dataValidationList.createPromptBox("下拉选择提示", "请使用下拉方式选择合适的值!");
// 设置输入错误提示信息
dataValidationList.createErrorBox("选择错误提示", "你输入的值未在备选列表中,请下拉选择合适的值!");
return dataValidationList;
}
/**
* 计算formula
* @param offset 偏移量,如果给0,表示从A列开始,1,就是从B列
* @param rowId 第几行
* @param colCount 一共多少列
* @return 如果给入参 1,1,10. 表示从B1-K1。最终返回 $B$1:$K$1
*/
private static String getRange(int offset, int rowId, int colCount) {
char start = (char) ('A' + offset);
if (colCount <= 25) {
char end = (char) (start + colCount - 1);
return "$" + start + "$" + rowId + ":$" + end + "$" + rowId;
} else {
char endPrefix = 'A';
char endSuffix = 'A';
// 26-51之间,包括边界(仅两次字母表计算)
if ((colCount - 25) / 26 == 0 || colCount == 51) {
// 边界值
if ((colCount - 25) % 26 == 0) {
endSuffix = (char) ('A' + 25);
} else {
endSuffix = (char) ('A' + (colCount - 25) % 26 - 1);
}
} else {// 51以上
if ((colCount - 25) % 26 == 0) {
endSuffix = (char) ('A' + 25);
endPrefix = (char) (endPrefix + (colCount - 25) / 26 - 1);
} else {
endSuffix = (char) ('A' + (colCount - 25) % 26 - 1);
endPrefix = (char) (endPrefix + (colCount - 25) / 26);
}
}
return "$" + start + "$" + rowId + ":$" + endPrefix + endSuffix + "$" + rowId;
}
}
}
执行方法
package com.example.mybatismysql8demo.controller;
import com.alibaba.excel.EasyExcel;
import com.example.mybatismysql8demo.config.SpinnerWriteHandler;
import com.example.mybatismysql8demo.excel.CityInfo;
import com.example.mybatismysql8demo.excel.GoodsExportExcel;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
@RestController
public class EasyExcelController {
@RequestMapping(value = "easyExcelExport", method = RequestMethod.GET)
public void easyExcelExport(HttpServletResponse response) {
try {
//一级下拉框
Map<Integer, String[]> mapDropDown = new HashMap<>(1);
mapDropDown.put(0, new String[]{"小明","小红"});
//多级下拉框
Map<Integer,Object> mapAll = new LinkedHashMap<>();
//一级数据
List<String> firstLevel = Arrays.asList("江西省", "河南省");
//一、二级数据名称
List<String> secondLevel = Arrays.asList("江西省", "南昌市", "抚州市", "河南省", "周口市");
//父级对应的子级数据
Map<String,List<String>> thirdLevel = new LinkedHashMap<>();
//依次列出各省的市、各市的县(对应级下的子级数据)
thirdLevel.put("江西省", Arrays.asList("南昌市", "抚州市"));
thirdLevel.put("河南省",Collections.singletonList("周口市"));
thirdLevel.put("南昌市",Arrays.asList("东湖区","西湖区"));
thirdLevel.put("抚州市",Arrays.asList("临川区","崇仁区"));
thirdLevel.put("周口市",Arrays.asList("川汇区","扶沟县"));
Map<String,Object> categoryMap = new HashMap<String,Object>(4);
categoryMap.put("firstLevel",firstLevel);
categoryMap.put("secondLevel",secondLevel);
categoryMap.put("thirdLevel",thirdLevel);
//1表示一级分类字段下标(1-3都是联动的)
mapAll.put(1,categoryMap);
//告诉浏览器数据格式,将头和数据传到前台
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("地区信息表.xls", "UTF-8"));
EasyExcel.write(response.getOutputStream(), CityInfo.class)
.autoCloseStream(Boolean.FALSE)
.registerWriteHandler(new SpinnerWriteHandler(mapAll,mapDropDown,1))
.sheet("地区信息")
.doWrite(Collections.EMPTY_LIST);
} catch (IOException e) {
e.printStackTrace();
}
}
}