0 写在前面
经常写数据导出到EXCEL,没有模板的情况下使用POI技术。以此作为记录,以后方便使用。
1 依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>1.4</version>
</dependency>
2 工具类
样式工具:
import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
import org.apache.poi.hssf.usermodel.HeaderFooter;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.*;
import java.io.UnsupportedEncodingException;
public class ExcelStyleUtils {
/**
* 生成标题样式
* @param workbook
* @return
*/
public static XSSFCellStyle genTitleStyle(XSSFWorkbook workbook) {
// 创建标题样式
XSSFCellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setWrapText(true);
// 标题居中,没有边框,所以这里没有设置边框,设置标题文字样式
XSSFFont titleFont = workbook.createFont();
// 加粗
titleFont.setBold(true);
// 文字尺寸
titleFont.setFontHeight((short) 15);
titleFont.setFontHeightInPoints((short) 15);
style.setFont(titleFont);
return style;
}
/**
* 生成表头样式
* @param workbook
* @return
*/
public static XSSFCellStyle genTableStyle(XSSFWorkbook workbook) {
// 创建表头样式
XSSFCellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setWrapText(true);
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());
style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 设置字体样式
Font headerFont = workbook.createFont();
headerFont.setFontName("Arial");
headerFont.setFontHeightInPoints((short) 10);
headerFont.setBold(true);
headerFont.setColor(IndexedColors.WHITE.getIndex());
style.setFont(headerFont);
return style;
}
/**
* 创建文本样式
* @param workbook
* @return
*/
public static XSSFCellStyle genContextStyle(XSSFWorkbook workbook) {
XSSFCellStyle style = workbook.createCellStyle();
// 文本水平居中显示
style.setAlignment(HorizontalAlignment.CENTER);
// 文本竖直居中显示
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 文本自动换行
style.setWrapText(true);
//生成Excel表单,需要给文本添加边框样式和颜色
/*
CellStyle.BORDER_DOUBLE 双边线
CellStyle.BORDER_THIN 细边线
CellStyle.BORDER_MEDIUM 中等边线
CellStyle.BORDER_DASHED 虚线边线
CellStyle.BORDER_HAIR 小圆点虚线边线
CellStyle.BORDER_THICK 粗边线
*/
// 设置文本边框
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
// 设置文本边框颜色
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
// 设置字体样式
XSSFFont contextFont = workbook.createFont();
contextFont.setFontName("Arial");
// 文字尺寸
contextFont.setFontHeightInPoints((short) 10);
style.setFont(contextFont);
return style;
}
/**
* 设置表单,并生成表单
* @param workbook
* @param sheetName
* @return
*/
public static XSSFSheet genSheet(XSSFWorkbook workbook, String sheetName){
// 生成表单
XSSFSheet sheet = workbook.createSheet(sheetName);
// 设置表单文本居中
sheet.setHorizontallyCenter(true);
sheet.setFitToPage(false);
// 打印时在底部右边显示文本页信息
Footer footer = sheet.getFooter();
footer.setRight( "Page " + HeaderFooter.numPages()+ " Of "+ HeaderFooter.page());
// 打印时在头部右边显示Excel创建日期信息
Header header = sheet.getHeader();
header.setRight("Create Date " + HeaderFooter.date() + " " + HeaderFooter.time());
// 设置打印方式
XSSFPrintSetup ps = sheet.getPrintSetup();
ps.setLandscape(true); // true:横向打印,false:竖向打印 ,因为列数较多,推荐在打印时横向打印
ps.setPaperSize(HSSFPrintSetup.A4_PAPERSIZE); // 打印尺寸大小设置为A4纸大小
return sheet;
}
/**
* 自适应宽度处理
*/
public static void setAutoColumnWidth(XSSFSheet sheet, int maxColumnNum) {
try {
for (int columnNum = 0; columnNum <= maxColumnNum; columnNum++) {
int columnWidth = sheet.getColumnWidth(columnNum) / 256;
for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
Row currentRow;
if (sheet.getRow(rowNum) == null) {
currentRow = sheet.createRow(rowNum);
} else {
currentRow = sheet.getRow(rowNum);
}
if (currentRow.getCell(columnNum) != null) {
Cell currentCell = currentRow.getCell(columnNum);
int length = currentCell.toString().getBytes("GBK").length;
if (columnWidth < length + 1) {
columnWidth = length + 1;
}
}
}
int maxWith = 128 * 255;
if (columnWidth * 300 > maxWith) {
sheet.setColumnWidth(columnNum, maxWith);
} else {
sheet.setColumnWidth(columnNum, columnWidth * 300);
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 设置某些列的值只能sheet中某列输入预制的数据,显示下拉框.
*
* @param sheet 模板sheet页(需要设置下拉框的sheet)
* @param sheetName 隐藏的sheet页,用于存放下拉框的值 (下拉框值对应一列)
* @param lastRow 存放下拉框值的最后一行
* @param col 存放下拉框值的列名 "A"
* @param firstRow 添加下拉框对应开始行
* @param endRow 添加下拉框对应结束行
* @param firstCol 添加下拉框对应开始列
* @param endCol 添加下拉框对应结束列
* @return XSSFSheet 设置好的sheet.
*/
public static XSSFSheet setXSSFValidation(XSSFSheet sheet, String sheetName, int lastRow, String col, int firstRow, int endRow, int firstCol, int endCol) {
// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
// 下拉框列
String cell = sheetName + "!$" + col + "$2:$" + col + "$" + lastRow;
// 创建验证对象
DataValidationHelper helper = sheet.getDataValidationHelper();
DataValidationConstraint constraint = helper.createFormulaListConstraint(cell);
// 这句话是关键 引用ShtDict的单元格
DataValidation dataValidate = helper.createValidation(constraint, regions);
dataValidate.setSuppressDropDownArrow(true);
dataValidate.createErrorBox("提示", "请输入规定范围内容");
dataValidate.setShowErrorBox(true);
sheet.addValidationData(dataValidate);
return sheet;
}
}
处理工具Java接口
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SimpleExcel {
/**
* 导出到Excel中的名字
*/
String labelName() default "";
/**
* 自定义日期格式, 如: yyyy-MM-dd
*/
String dateFormat() default "";
/**
* 导出数据样式, 居中:左对齐..
*/
HorizontalAlignment alignStyle() default HorizontalAlignment.CENTER;
}
水印工具
/**
* Excel 添加水印。支持 SXSSFWorkbook 和 XSSFWorkbook 模式
*/
public class WaterMarkUtil {
/**
* Excel 导出添加水印
* @param workbook ExcelWorkbook
*/
public static void insertWaterMarkTextToXlsx(Workbook workbook, String waterMarkText) throws IOException {
// 根据工作博类型两种方式添加水印
if (workbook instanceof SXSSFWorkbook) {
insertWaterMarkTextToXlsx((SXSSFWorkbook) workbook, waterMarkText);
} else if (workbook instanceof XSSFWorkbook) {
insertWaterMarkTextToXlsx((XSSFWorkbook) workbook, waterMarkText);
}
//throw new RemoteException("HSSFWorkbook 模式不支持 Excel 水印");
}
/**
* 给 Excel 添加水印
* @param workbook SXSSFWorkbook
* @param waterMarkText 水印文字内容
*/
public static void insertWaterMarkTextToXlsx(SXSSFWorkbook workbook, String waterMarkText) throws IOException {
BufferedImage image = createWatermarkImage(waterMarkText);
ByteArrayOutputStream imageOs = new ByteArrayOutputStream();
ImageIO.write(image, "png", imageOs);
int pictureIdx = workbook.addPicture(imageOs.toByteArray(), XSSFWorkbook.PICTURE_TYPE_PNG);
XSSFPictureData pictureData = (XSSFPictureData) workbook.getAllPictures().get(pictureIdx);
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
//获取每个Sheet表
SXSSFSheet sheet = workbook.getSheetAt(i);
//这里由于 SXSSFSheet 没有 getCTWorksheet() 方法,通过反射取出 _sh 属性
XSSFSheet shReflect = (XSSFSheet) ReflectUtil.getFieldValue(sheet, "_sh");
PackagePartName ppn = pictureData.getPackagePart().getPartName();
String relType = XSSFRelation.IMAGES.getRelation();
PackageRelationship pr = shReflect.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
shReflect.getCTWorksheet().addNewPicture().setId(pr.getId());
}
}
/**
* 给 Excel 添加水印
* @param workbook XSSFWorkbook
* @param waterMarkText 水印文字内容
*/
public static void insertWaterMarkTextToXlsx(XSSFWorkbook workbook, String waterMarkText) throws IOException {
BufferedImage image = createWatermarkImage(waterMarkText);
ByteArrayOutputStream imageOs = new ByteArrayOutputStream();
ImageIO.write(image, "png", imageOs);
int pictureIdx = workbook.addPicture(imageOs.toByteArray(), XSSFWorkbook.PICTURE_TYPE_PNG);
XSSFPictureData pictureData = workbook.getAllPictures().get(pictureIdx);
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
//获取每个Sheet表
XSSFSheet sheet = workbook.getSheetAt(i);
PackagePartName ppn = pictureData.getPackagePart().getPartName();
String relType = XSSFRelation.IMAGES.getRelation();
PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
}
}
/**
* 创建水印图片
* @param waterMark 水印文字
*/
public static BufferedImage createWatermarkImage(String waterMark) {
String[] textArray = waterMark.split("\n");
Font font = new Font("microsoft-yahei", Font.PLAIN, 32);
int width = 500;
int height = 400;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 背景透明 开始
Graphics2D g = image.createGraphics();
image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g.dispose();
// 背景透明 结束
g = image.createGraphics();
g.setColor(new Color(Color.lightGray.getRGB()));// 设定画笔颜色
g.setFont(font);// 设置画笔字体
// g.shear(0.1, -0.26);// 设定倾斜度
//设置字体平滑
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//文字从中心开始输入,算出文字宽度,左移动一半的宽度,即居中
FontMetrics fontMetrics = g.getFontMetrics(font);
// 水印位置
int x = width / 2;
int y = height / 2;
// 设置水印旋转
g.rotate(Math.toRadians(-40), x, y);
for (String s : textArray) {
// 文字宽度
int textWidth = fontMetrics.stringWidth(s);
g.drawString(s, x - (textWidth / 2), y);// 画出字符串
y = y + font.getSize();
}
g.dispose();// 释放画笔
return image;
}
/**
* 设置打印的参数
* @param wb XSSFWorkbook
*/
public static void setPrintParams(XSSFWorkbook wb) {
XSSFSheet sheet = wb.getSheetAt(0);
XSSFPrintSetup printSetup = sheet.getPrintSetup();
// 打印方向,true:横向,false:纵向(默认
printSetup.setLandscape(true);
//设置A4纸
printSetup.setPaperSize(XSSFPrintSetup.A4_PAPERSIZE);
// 将整个工作表打印在一页(缩放),如果行数很多的话,可能会出问题
// sheet.setAutobreaks(true);
//将所有的列调整为一页,行数多的话,自动分页
printSetup.setScale((short) 70);//缩放的百分比,自行调整
sheet.setAutobreaks(false);
}
}
导出Excel工具类
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* Excel的工具类
*/
public class SimpleExcelUtil<T> {
/**
* 工作薄
*/
private XSSFWorkbook wb;
/**
* 工作表
*/
private XSSFSheet sheet;
/**
* 需要导出的数据
*/
private List<T> exportList;
/**
* 对象的class对象
*/
private Class<T> clazz;
/**
* 被选中需要导出的字段名称
*/
private Map<String, Object> checkedFieldsName;
/**
* 被选中需要导出的字段对象
*/
private List<Field> checkedFields;
private SimpleExcelUtil() {
}
public SimpleExcelUtil(Class<T> clazz) {
this.clazz = clazz;
}
/**
* 导出Excel
* @param list 导出数据列表
* @param fieldsName 选中需要导出的字段
* @param sheetName sheet页名称
*/
public void exportExcel(HttpServletResponse response, List<T> list, Map<String, Object> fieldsName, String sheetName, String labelTitle, Boolean isWaterMark, String waterMarkText) {
// 初始化数据
init(list, sheetName, fieldsName);
// 加入标题数据
createTopTitleRow(labelTitle);
// 在标题下方加入表头数据
createTopRow();
try {
// 在表头下方,添加目标数据
createOtherRow();
// 调整自适应列宽
setAutoColumnWidth(sheet, checkedFields.size());
// 添加水印
try {
if (isWaterMark) {
WaterMarkUtil.insertWaterMarkTextToXlsx(wb, waterMarkText);
}
} catch (Exception e) {
throw new Exception("添加水印失败");
}
} catch (Exception e) {
e.printStackTrace();
}
// 生成excel文件并入response
try {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
response.setHeader("Content-Disposition","attachment;filename=" + new String((labelTitle + ".xlsx").getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
wb.write(response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
wb.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 将数据导出Excel(重载,自定义导出列)
* @param response
* @param list 需要导出的数据
* @param fieldsName 自定义导出字段
* @param labelTitle 首行标题内容
*/
public void exportExcel(HttpServletResponse response, List<T> list, Map<String, Object> fieldsName, String labelTitle) {
exportExcel(response, list, fieldsName, null, labelTitle, false, null);
}
/**
* 导出含有水印的Excel
* @param response
* @param labelTitle 首行标题内容
* @param list 需要导出的数据
* @param fieldsName 自定义导出字段
* @param waterMarkText 水印内容
*/
public void exportWaterMarkExcel(HttpServletResponse response, List<T> list, Map<String, Object> fieldsName, String labelTitle, String waterMarkText) {
exportExcel(response, list, fieldsName, null, labelTitle, true, waterMarkText);
}
/**
* sheet页第一行加入导出的标题数据
*/
private void createTopTitleRow(String title) {
Row row = sheet.createRow(0);
// 设定固定行高
row.setHeightInPoints(30);
Cell cell = row.createCell(0);
//前端只传一个值合并单元格报错
if (checkedFields.size() > 1) {
// 对标题所处列进行单元格的合并(对应下面导出的字段列数)
CellRangeAddress region = new CellRangeAddress(0, 0, 0, checkedFields.size() - 1);
sheet.addMergedRegion(region);
}
// 设置单元格样式
CellStyle style = wb.createCellStyle();
// 设置单元格水平样式
style.setAlignment(HorizontalAlignment.CENTER);
// 设置单元格垂直样式
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 设置字体样式
Font font = wb.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 15);
font.setFontName("Arial");
font.setColor(IndexedColors.BLACK.getIndex());
style.setFont(font);
cell.setCellValue(title);
cell.setCellStyle(style);
}
/**
* sheet第二行加入表头数据
*/
private void createTopRow() {
Row row = sheet.createRow(1);
// 设定固定行高
row.setHeightInPoints(20);
for (int index = 0; index < checkedFields.size(); index++) {
Cell cell = row.createCell(index);
cell.setCellValue(checkedFields.get(index).getAnnotation(SimpleExcel.class).labelName());
// 表头格式
CellStyle style = wb.createCellStyle();
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());
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Font headerFont = wb.createFont();
headerFont.setFontName("Arial");
headerFont.setFontHeightInPoints((short) 10);
headerFont.setBold(true);
headerFont.setColor(IndexedColors.WHITE.getIndex());
style.setFont(headerFont);
cell.setCellStyle(style);
}
}
/**
* 添加导出数据
*/
private void createOtherRow() throws IllegalAccessException {
// 导出数据格式
CellStyle style = wb.createCellStyle();
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);
for (int rowNum = 2; rowNum <= exportList.size() + 1; rowNum++) {
Row row = sheet.createRow(rowNum);
row.setHeightInPoints(18);
T t = exportList.get(rowNum - 2);
for (int colNum = 0; colNum < checkedFields.size(); colNum++) {
Cell cell = row.createCell(colNum);
Field field = checkedFields.get(colNum);
field.setAccessible(true);
// 处理不同单元格字段对应不同样式
if (colNum != 0) {
CellStyle cStyle = wb.createCellStyle();
cStyle.cloneStyleFrom(row.getCell(colNum - 1).getCellStyle());
cStyle.setAlignment(checkedFields.get(colNum).getAnnotation(SimpleExcel.class).alignStyle());
cell.setCellStyle(cStyle);
addCell(cell, field, t);
continue;
}
// 单元格水平样式自定义
style.setAlignment(checkedFields.get(colNum).getAnnotation(SimpleExcel.class).alignStyle());
cell.setCellStyle(style);
// 单元格设置值
addCell(cell, field, t);
}
}
}
/**
* 单元格中添加数据
* @param cell 单元格
* @param field 字段
* @param t list中的一条数据
*/
private void addCell(Cell cell, Field field, T t) throws IllegalAccessException {
Class<?> fieldType = field.getType();
if (String.class == fieldType && field.get(t) != null) {
cell.setCellValue((String) field.get(t));
} else if (((Integer.TYPE == fieldType) || (Integer.class == fieldType)) && field.get(t) != null) {
cell.setCellValue((Integer) field.get(t));
} else if (((Long.TYPE == fieldType) || (Long.class == fieldType)) && field.get(t) != null) {
cell.setCellValue((Long) field.get(t));
} else if (((Double.TYPE == fieldType) || (Double.class == fieldType)) && field.get(t) != null) {
cell.setCellValue((Double) field.get(t));
} else if (((Float.TYPE == fieldType) || (Float.class == fieldType)) && field.get(t) != null) {
cell.setCellValue((Float) field.get(t));
} else if (BigDecimal.class == fieldType && field.get(t) != null) {
cell.setCellValue(((BigDecimal) field.get(t)).toString());
} else if (Date.class == fieldType && field.get(t) != null) {
String dateFormat = field.getAnnotation(SimpleExcel.class).dateFormat();
cell.setCellValue(dateFormat((Date) field.get(t), dateFormat));
}
}
/**
* 时间格式转换
* @param date 日期
* @param dateFormat 日期格式
* @return
*/
private String dateFormat(Date date, String dateFormat) {
// 默认格式
if (dateFormat == null || "".equals(dateFormat)) {
dateFormat = "yyyy-MM-dd";
}
SimpleDateFormat df = new SimpleDateFormat(dateFormat);
return df.format(date);
}
/**
* 初始化
*/
public void init(List<T> list, String sheetName, Map<String, Object> fieldsName) {
this.exportList = list;
this.checkedFieldsName = fieldsName;
// 初始化导出数据
initExportList();
// 初始化工作薄
initWorkbook();
// 初始化工作表
initSheet(sheetName == null ? "Sheet1" : sheetName);
// 初始化待导出的字段
initFields();
}
/**
* 初始化导出数据
*/
private void initExportList() {
// 防止导出过程中出现空指针
if (Objects.isNull(this.exportList)) {
this.exportList = new ArrayList<>();
}
}
/**
* 初始化工作簿
*/
private void initWorkbook() {
this.wb = new XSSFWorkbook();
}
/**
* 初始化工作表
*/
private void initSheet(String sheetName) {
this.sheet = wb.createSheet(sheetName);
}
/**
* 初始化checkedFields
* checkedFields用户选中的字段
* 1.如果checkedFieldsName没有定义(未自定义导出字段),所有字段全部导出
* 2.如果checkedFieldsName进行了定义,根据定义字段进行导出
*/
private void initFields() {
// 获取对象所有字段对象
Field[] fields = clazz.getDeclaredFields();
// 过滤出checkedFields
this.checkedFields = Arrays.asList(fields).stream().
filter(item -> {
if (!Objects.isNull(this.checkedFieldsName)) {
if (item.isAnnotationPresent(SimpleExcel.class)) {
return checkedFieldsName.containsKey(item.getName());
}
} else {
return item.isAnnotationPresent(SimpleExcel.class);
}
return false;
}).collect(Collectors.toList());
}
/**
* 自适应宽度处理
*/
private static void setAutoColumnWidth(XSSFSheet sheet, int maxColumnNum) {
try {
for (int columnNum = 0; columnNum <= maxColumnNum; columnNum++) {
int columnWidth = sheet.getColumnWidth(columnNum) / 256;
for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
Row currentRow;
if (sheet.getRow(rowNum) == null) {
currentRow = sheet.createRow(rowNum);
} else {
currentRow = sheet.getRow(rowNum);
}
if (currentRow.getCell(columnNum) != null) {
Cell currentCell = currentRow.getCell(columnNum);
int length = currentCell.toString().getBytes("GBK").length;
if (columnWidth < length + 1) {
columnWidth = length + 1;
}
}
}
int maxWith = 128 * 255;
if (columnWidth * 300 > maxWith) {
sheet.setColumnWidth(columnNum, maxWith);
} else {
sheet.setColumnWidth(columnNum, columnWidth * 300);
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
3 测试代码
与实际复杂业务不同 在此我们只做模拟
3.1实体User 类
import lombok.Data;
@Data
public class User {
// 姓名
private String name;
// 性别
private String sex;
// 年龄
private Integer age;
// 邮箱
private String email;
// 生日
private String birthday;
// 住址
private String address;
public User(String name, String sex, Integer age, String email, String birthday, String address) {
this.name = name;
this.sex = sex;
this.age = age;
this.email = email;
this.birthday = birthday;
this.address = address;
}
}
Controller
import com.example.demo17.service.ExcelTestService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
@RestController
@RequestMapping("/test")
public class ExcelTestController {
@Resource
private ExcelTestService excelTestService;
@GetMapping("/export-excel")
public void export(HttpServletResponse response) {
excelTestService.exportExcel(response);
}
}
Service
import javax.servlet.http.HttpServletResponse;
public interface ExcelTestService {
void exportExcel(HttpServletResponse response);
}
import com.example.demo17.demos.web.User;
import com.example.demo17.util.ExcelStyleUtils;
import com.example.demo17.util.WaterMarkUtil;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @Classname ExcelTestServiceImpl
* @Date 2024/7/18 14:30
* @Created by Yue Teng
*/
@Service("ExcelTestService")
public class ExcelTestServiceImpl implements ExcelTestService{
@Override
public void exportExcel(HttpServletResponse response) {
// 模拟数据
User user1 = new User("岳腾", "男", 23, "ateng1118@163.com", "2001-02-17", "泰山区山东出版(泰山)文创设计产业园");
User user2 = new User("岳公凯", "男", 23, "fengyexieyang@gmail.com", "2001-03-01", "淄博市高新区仪器仪表产业园");
User user3 = new User("路舜", "男", 22, "1078550822@qq.com", "2002-06-13", "槐荫区华图教育");
User user4 = new User("王尊贞", "男", 24, "2323189192@qq.com", "2000-12-25", "历城区中建凤栖第");
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
// 初始化导出文件名字
String labelTitle = "人员表.xlsx";
//设置导出格式
String excelName = new String(labelTitle.getBytes(), StandardCharsets.ISO_8859_1);
// 创建工作簿
XSSFWorkbook excel = new XSSFWorkbook();
// 绘制第一个sheet -> 人员表
creatUserSheet(excel,users);
// 生成excel文件并入response
try {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + excelName);
excel.write(response.getOutputStream());
excel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 绘制sheet
* @param excel
* @param users
*/
private void creatUserSheet(XSSFWorkbook excel , List<User> users) {
// 设置sheet名称
XSSFSheet sheet = excel.createSheet();
// 此处sheetIndex的参数代表值绘制的第几个sheet页,如果有绘制多个sheet的需求,则0,1,2,3,4……这样创建,不同的sheet页可以复用一个方法或者单独写不同的方法
excel.setSheetName(0, "人员");
XSSFCellStyle header = ExcelStyleUtils.genContextStyle(excel);
header.setFillPattern(FillPatternType.SOLID_FOREGROUND); //设置填充方案
header.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); //设置填充颜色
// 创建标题
XSSFRow titleRow = sheet.createRow(0);
XSSFCell titleCell = titleRow.createCell(0);
// 合并标题单元格 4个参数分别为 起始行 截止行 起始列 截止列
sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, 6));
titleCell.setCellValue("人员明细");
// 设置标题样式
XSSFCellStyle titleStyle = ExcelStyleUtils.genTitleStyle(excel);
titleCell.setCellStyle(titleStyle);
// 创建表头1
XSSFRow tableRow1 = sheet.createRow(2);
// 创建表头样式
XSSFCellStyle tableStyle = ExcelStyleUtils.genTableStyle(excel);
// 组装表头数据
String[] tableTitleInit = {
"序号", "姓名", "性别", "年龄", "邮箱", "生日" , "住址"};
for (int i = 0; i < tableTitleInit.length; i++) {
XSSFCell tableRowCell = tableRow1.createCell(i);
tableRowCell.setCellStyle(tableStyle);
tableRowCell.setCellValue(tableTitleInit[i]);
}
// 对以上数据设置自适应宽度
ExcelStyleUtils.setAutoColumnWidth(sheet, 6);
// 主数据内容导出
// 记录行数从表头下方(第四行)开始
int rowNum = 3;
// 创建文本样式
XSSFCellStyle contextStyle = ExcelStyleUtils.genContextStyle(excel);
for (int i = 0; i < users.size(); i++) {
// 创建数据行
XSSFRow tableRow = sheet.createRow(rowNum);
// 取导出数据
User data = users.get(i);
// 进行逐列单元格塞入数据
for (int j = 0; j < 7; j++) {
XSSFCell rowCell = tableRow.createCell(j);
rowCell.setCellStyle(contextStyle);
switch (j) {
case 0:
// 序号
rowCell.setCellValue(i + 1);
break;
case 1:
// 姓名
rowCell.setCellValue(data.getName());
break;
case 2:
// 性别
rowCell.setCellValue(data.getSex());
break;
case 3:
// 年龄
rowCell.setCellValue(data.getAge());
break;
case 4:
// 邮箱
rowCell.setCellValue(data.getEmail());
break;
case 5:
// 生日
rowCell.setCellValue(birthdayConvert(data.getBirthday()));
break;
case 6:
// 住址
rowCell.setCellValue(data.getAddress());
break;
}
}
rowNum++;
}
// 对以上数据设置自适应宽度
ExcelStyleUtils.setAutoColumnWidth(sheet, 7);
// 添加水印
try {
// 定义水印
String waterMarkText = "我是水印";
WaterMarkUtil.insertWaterMarkTextToXlsx(excel, waterMarkText);
} catch (Exception e) {
try {
throw new Exception("添加水印失败");
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
/**
* 生日转换,只用来演示在上方switch - case 语句内部可以转换数据
* @param birthday
* @return
*/
public String birthdayConvert(String birthday){
Date date = null;
try {
date = new SimpleDateFormat("yyyy-MM-dd").parse(birthday);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return new SimpleDateFormat("yyyy年MM月dd日").format(date);
}
}
4 导出测试
使用Postman进行接口测试,没接触过Postman的小伙伴可以看我这篇博客Postman导出excel文件
保存为文件
5 写在末尾
可以看到导出很成功,包括水印 sheet页名称自适应宽度。还有一些高亮……等功能可以直接搜索使用