Java从零开始实现导出excel(三)
新人博主,随笔记录。主要还是通过自己日常工作积累,系统整理一些技术。希望能够节省更多人的时间,走出自己的路。
简述
新的一年,先祝各位小伙伴新的一年,事业有成,身体健康,最重要的暴富发财。
这是年后的第一篇文章,其实篇文章我纠结了很久,excel的导出我的前两篇文章基本都够用了。还有些复杂的场景:复杂表头和复杂数据。
同样我去研究了EasyExcel官方文档,个人感觉在这两个方面并没有太多提升。而我看我用的复杂表头技术,和网上的都差不多大家伙都能搜索到。我这里就不再重复写了,给两个链接小伙伴们可以去看看。
poi进行excel复杂表头导出
POI导出复杂表头
这篇文章主要是实现复杂数据,以及excel中颜色的使用。当然这一次我们用的是POI,不再使用EasyExcel。主要的思想就是使用POI在excel表格中一行一行的写入。
环境搭建
这一篇我们用同一个工程。不过我们需要添加新的jar包,这次我们使用的是POI。不知道工程搭建的小伙伴可以去瞟一眼我的第一篇文章Java从零开始实现导出excel(一)
需要使用到的jar包(这里我都是用maven注入的)
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17-beta1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
这里我们需要特别注意的一点:
同时引入EasyExecl的jar包 和 POI jar包时会有 jar包冲突所以jar包的版本得是 3.17-beta1.
下面是使用POI实现excel的导出
这次首先说明我们需要模拟的场景:
在一个sheet页中,有两个不同的表格,给表格中相同的数据标上相同的底色。
这里我用的是:两个不同的学生成绩表,成绩相同的学生底色相同
- 添加另外一个学生实体
- 编写使用poi导出的方法
- 编写ExcelStyle类,控制excel表格的样式
- 编写导出的controller
学生实体
/**
* 学生2
*/
@Data
public class Student2 {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 性别
*/
private String sex;
/**
* 成绩
*/
private Integer fraction;
}
导出的controller
/**
* 使用poi导出excel
*
* 两个不同的学生成绩表,成绩相同的学生底色相同
* @param response
* @throws Exception
*/
@GetMapping(value = "/exportPoiExcel")
public void exportPoiExcel(HttpServletResponse response) throws Exception {
String title = "学生成绩对比表";
String[] headers1 = {"姓名","年龄","性别","学号","家庭地址","成绩"};
String[] headers2 = {"姓名","性别","性别","成绩"};
ExcelUtil.fillExcelData(getData(), getData1(), headers1, headers2, title, response);
}
这里的我们用的字符串数组装两个表得表头;
poi导出的方法
/**
* 利用反射机制遍历对象的属性值,并放入单元格中,为excel添加样式
*/
public static void fillExcelData(List<Student> list, List<Student2> list2, String[] headers, String[] headers2, String title, HttpServletResponse response) throws Exception {
// 创建工作薄
HSSFWorkbook wb = new HSSFWorkbook();
// 生成一个表格
HSSFSheet sheet = wb.createSheet(title);
// 设置表格默认宽度为15个字节
sheet.setDefaultColumnWidth((short) 25);
//颜色控制器
Map<String, Integer> colorMap = new HashMap<>();
int colorIndex = 10;//颜色索引
int moreColorIndex = 0;//超过索引
ExcelStyle excelStyle = new ExcelStyle(wb);
CellStyle style = excelStyle.getTitleStyle(wb);//获取标题样式
CellStyle style2 = excelStyle.getHeaderStyle(wb);//获取表头样式
CellStyle style3 = excelStyle.getHeaderExplainStyle(wb);//获取表头说明样式
// 标题
int rowIndex = 0;//行数索引
// 合并单元格0到headers.length -1行
sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 0, headers.length - 1));
HSSFRow row0 = sheet.createRow(rowIndex++);
row0.setHeightInPoints(26);
HSSFCell cell1 = row0.createCell(0);
cell1.setCellStyle(style);
cell1.setCellValue(title);
for (short i = 1; i < headers.length; i++) {
HSSFCell cell = row0.createCell(i);
cell.setCellStyle(style);
}
// 合并单元格0到headers.length -2行
sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 0, headers.length - 1));
//添加列表说明
HSSFRow row1 = sheet.createRow(rowIndex++);
row1.setHeightInPoints(16);
HSSFCell cell2 = row1.createCell(0);
cell2.setCellStyle(style3);
cell2.setCellValue("1.列表格式一");
for (short i = 1; i < headers.length; i++) {
HSSFCell cell = row1.createCell(i);
cell.setCellStyle(style3);
}
// 产生表格表头行
HSSFRow row = sheet.createRow(rowIndex++);
row.setHeightInPoints(20);
for (short i = 0; i < headers.length; i++) {
HSSFCell cell = row.createCell(i);
cell.setCellStyle(style2);
cell.setCellValue(headers[i]);
}
// 产生表格内容行
if (list != null && list.size() > 0) {
for (Student obj : list) {
// 利用反射得到对象的属性
Class cls = obj.getClass();
java.lang.reflect.Field[] flds = cls.getDeclaredFields(); // 得到私有属性
row = sheet.createRow(rowIndex++);
row.setHeightInPoints(30);//设置行高
CellStyle style4 = excelStyle.getListContentStyle(wb);//获取列表内容样式
//判断是否有相同成绩得学生
List<Student2> collect2 = list2.stream().filter(data -> data.getFraction().equals(obj.getFraction())).collect(Collectors.toList());
List<Student> collect1 = list.stream().filter(data -> data.getFraction().equals(obj.getFraction())).collect(Collectors.toList());
if (collect2.size() + collect1.size() > 1 && colorMap.get(String.valueOf(obj.getFraction())) == null) {
colorMap.put(String.valueOf(obj.getFraction()), colorIndex);//加入颜色控制器
colorIndex++;
if (colorIndex == 27) {//调色板中..27,32-39的索引为空
colorIndex++;
}
if (colorIndex > 31 && colorIndex < 40) {//调色板中.31-39的索引为空
colorIndex = 40;
}
if (colorIndex > 64) {//调色板最大索引64
moreColorIndex = 9;
}
}
if (colorMap.get(String.valueOf(obj.getFraction())) != null || colorIndex > 64) {//超过调色板的项目数据底色都为白色
style4.setFillForegroundColor(IndexedColors.fromInt(moreColorIndex == 0 ? colorMap.get(String.valueOf(obj.getFraction())) : moreColorIndex).index);
style4.setFillPattern(FillPatternType.SOLID_FOREGROUND);
}
for (int j = 0; j < headers.length; j++) {
flds[j].setAccessible(true);
HSSFCell cell = row.createCell(j);
cell.setCellStyle(style4);
cell.setCellValue((flds[j].get(obj)).toString());
}
}
}
//合并单元格
sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 0, headers2.length - 1));
//添加列表说明
HSSFRow row2 = sheet.createRow(rowIndex++);
row2.setHeightInPoints(16);
HSSFCell cell3 = row2.createCell(0);
cell3.setCellStyle(style3);
cell3.setCellValue("1.列表格式二");
for (short i = 1; i < headers2.length; i++) {
HSSFCell cell = row2.createCell(i);
cell.setCellStyle(style3);
}
// 产生表头行
HSSFRow row_cy = sheet.createRow(rowIndex++);
row_cy.setHeightInPoints(20);
for (short i = 0; i < headers2.length; i++) {
HSSFCell cell = row_cy.createCell(i);
cell.setCellStyle(style2);
cell.setCellValue(headers2[i]);
}
// 产生表格内容行
if (list2 != null && list2.size() > 0) {
for (Student2 obj : list2) {
// 利用反射得到对象的属性
Class cls = obj.getClass();
java.lang.reflect.Field[] flds = cls.getDeclaredFields(); // 得到私有属性
row = sheet.createRow(rowIndex++);
row.setHeightInPoints(30);//设置行高
CellStyle style4 = excelStyle.getListContentStyle(wb);//获取列表内容样式
//判断是否有相同成绩得学生
List<Student2> collectCy = list2.stream().filter(data -> data.getFraction().equals(obj.getFraction())).collect(Collectors.toList());
if (collectCy.size() > 1 && colorMap.get(String.valueOf(obj.getFraction())) == null) {
colorMap.put(String.valueOf(obj.getFraction()), colorIndex);//加入颜色控制器
colorIndex++;
if (colorIndex == 27) {//调色板中..27,32-39的索引为空
colorIndex++;
}
if (colorIndex > 31 && colorIndex < 40) {//调色板中.31-39的索引为空
colorIndex = 40;
}
if (colorIndex > 64) {//调色板最大索引64
moreColorIndex = 9;
}
}
if (colorMap.get(String.valueOf(obj.getFraction())) != null || colorIndex > 64) {//超过调色板的项目数据底色都为白色
style4.setFillForegroundColor(IndexedColors.fromInt(moreColorIndex == 0 ? colorMap.get(String.valueOf(obj.getFraction())) : moreColorIndex).index);
style4.setFillPattern(FillPatternType.SOLID_FOREGROUND);
}
//
for (int j = 0; j < headers2.length; j++) {
flds[j].setAccessible(true);
HSSFCell cell = row.createCell(j);
cell.setCellStyle(style4);
cell.setCellValue((flds[j].get(obj)).toString());
}
}
}
response.setContentType("multipart/form-data");
response.setCharacterEncoding("utf-8");
String fileName = "学生出成绩对比表" + ".xls";
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("gb2312"), "iso8859-1"));
response.setContentType("application/ynd.ms-excel;charset=UTF-8");
OutputStream out = response.getOutputStream();
wb.write(out);
out.flush();
out.close();
}
这个导出的方法是实现这个功能的关键:
-
这里面用到了反射机制获取对象中的属性。
-
在对列表内容进行处理的时候,这里用到了JDK1.8新特性 stream流
-
就是poi中颜色的注入,这里面有 坑。颜色索引从9-64其中27,32-39的索引是缺失的,使用的时候我们避开它们,否则会报空指针
颜色索引链接
ExcelStyle类
这个类内容比较多,我贴一点关键的方法,具体大家可以去我的源码中看。这个里面主要是继承两个父类:
ExcelExportStylerDefaultImpl
IExcelExportStyler
/**
* 生成标题样式
* @param workbook
* @return
*/
public CellStyle getTitleStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
// 前景色
style.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.index);
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setWrapText(true);
// 生成标题字体
Font font = getFont(workbook,(short) 16,true);
// 把字体应用到当前的样式
style.setFont(font);
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 workbook
* @return
*/
public CellStyle getHeaderStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFillForegroundColor(IndexedColors.LIME.index);
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Font font = getFont(workbook, FONT_SIZE_TWELVE, true);
// 把字体应用到当前的样式
style.setFont(font);
return style;
}
/**
* 生成表头说明样式
* @param workbook
* @return
*/
public CellStyle getHeaderExplainStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setAlignment(HorizontalAlignment.LEFT);
Font font = getFont(workbook, FONT_SIZE_TWELVE, false);
font.setColor(HSSFColor.RED.index);
// 把字体应用到当前的样式
style.setFont(font);
return style;
}
导出结果
源码地址
共有条评论 网友评论