现状
在日常Java开发中,有时会有业务需要根据已有的查询结果导出Excel文件。通常,他们会考虑使用开源 POI 库。大部分人应该先从网上找几个例子poi单元格颜色设定 程序人生,然后复制粘贴到项目中,根据需求设置对应的单元格内容,代码中会有很多自定义的代码,如下图:
对应的单元格分配
设计
一般导出的Excel第一行是表头信息,第二行是需要导出的数据。我们可以使用 Model 类来定义 Excel 标题和列信息。
// 实现Comparable接口是为了根据order字段排序,确定这个表头的标题放在列的位置顺序
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExcelHeader implements Comparable {
// 表头的标题名称
private String title;
// 表头的标题所在列的顺序
private int order;
// 单元格的宽度
private int width;
// 对应字段
private String fieldName;
@Override
public int compareTo(ExcelHeader o) {
return Integer.compare(order, o.order);
}
}
导出的数据是基于后端某个Model类的List集合。我们可以定义一个注解来标识这个Model需要导出的字段poi单元格颜色设定 程序人生,从而实现通用导出。
// 用来在导出对象的field上加入的注解,通过该annotation说明某个属性所对应的标题
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
public @interface ExcelResources {
// 导出字段的标题名称,用于构造ExcelHeader对象的title
String title();
// 导出字段在excel的顺序,用于构造ExcelHeader对象的order
int order() default 9999;
// 导出字段列的宽度,用于构造ExcelHeader对象的width
int width() default 150;
}
1、先定义工具类的通用静态导出方法
/**
* @param objs 导出的数据集合,一般为后端DB查询结果
* @param clz 导出的Excel内容Model类,其导出属性上需要加@ExcelResources
* @param sheetName 导出的Excel的Sheet页名称
* @param pageSize 导出的Excel的每个Sheet页的数据量,若objs.size()>pageSize,会写入到(objs.size()/pageSize+1)个Sheet页中
* @param fileName 导出的Excel文件名,会加上_yyyyMMddHHmmss避免重名
* @param response HttpServletResponse
*/
public static void exportExcel(List objs, Class clz, String sheetName, int pageSize, String fileName,
HttpServletResponse response)
throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
if (objs == null || clz == null) {
throw new NullPointerException("parameter must be not null");
}
OutputStream outputStream = null;
try {
Workbook wb = handleObj2Excel(objs, clz, sheetName, pageSize);
setResponseHeader(response, fileName);
// 定入到输出流
outputStream = response.getOutputStream();
wb.write(outputStream);
// 刷新流
outputStream.flush();
} finally {
IOUtils.closeQuietly(outputStream);
}
}
2、私有方法handleObj2Excel()会使用参数objs data和clz上的自定义注解来反映获取到的数据并设置到cell中,并返回HSSFWorkbook对象。
private static HSSFWorkbook handleObj2Excel(List objs, Class clz, String sheetName, int pageSize)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
HSSFWorkbook wb = new HSSFWorkbook();
// 获取excel标题列表并排序,详见下面私有方法
List headers = getHeaderList(clz);
Collections.sort(headers);
int size = objs.size();
if (size > 0) {
int sheetCount = size % pageSize == 0 ? size / pageSize : size / pageSize + 1;
HSSFSheet sheet;
// 设置第i个sheet页内的数据
for (int i = 1; i <= sheetCount; i++) {
// 创建sheet页
if (!StringUtils.isEmpty(sheetName)) {
sheet = wb.createSheet(sheetName + i);
} else {
sheet = wb.createSheet();
}
HSSFRow row = sheet.createRow(0);
// 设置head的数据格式,详见下面私有方法
CellStyle titleStyle = setCellStyle(wb, POSITION_TITLE);
for (int m = 0, len = headers.size(); m < len; m++) {
HSSFCell head = row.createCell(m);
head.setCellStyle(titleStyle);
head.setCellValue(headers.get(m).getTitle());
sheet.setColumnWidth(m, 40 * headers.get(m).getWidth());
}
// 设置body数据部分的格式
int begin = (i - 1) * pageSize;
int end = Math.min((begin + pageSize), objs.size());
int rowCount = 1;
CellStyle bodyStyle = setCellStyle(wb, POSITION_BODY);
for (int n = begin; n < end; n++) {
row = sheet.createRow(rowCount);
Object obj = objs.get(n);
for (int x = 0, len = headers.size(); x < len; x++) {
Cell body = row.createCell(x);
body.setCellStyle(bodyStyle);
// 反射获取值并set到对应的单元格中,
// objs中model的field与clz的field名称需要相同对应才能导出
body.setCellValue(BeanUtils.getProperty(obj, headers.get(x).getFieldName()));
}
rowCount++;
}
}
}
return wb;
}
// 由参数clz来获取@ExcelResources注解并生成表头信息ExcelHeader类的集合
private static List getHeaderList(Class clz) {
Field[] ms = clz.getDeclaredFields();
List headers = new ArrayList(ms.length);
for (Field m : ms) {
String field = m.getName();
if (m.isAnnotationPresent(ExcelResources.class)) {
ExcelResources er = m.getAnnotation(ExcelResources.class);
// 标题,序号,宽度,其成员变量
headers.add(new ExcelHeader(er.title(), er.order(), er.width(), field));
}
}
return headers;
}
// 设置单元格样式
private static CellStyle setCellStyle(Workbook workBook, String position) {
CellStyle cellStyle = workBook.createCellStyle();
cellStyle.setAlignment(HorizontalAlignment.GENERAL);
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 设置单元格字体,表头字段可以大些
Font headerFont = workBook.createFont();
if (POSITION_TITLE.equals(position)) {
headerFont.setFontHeightInPoints((short) 12);
headerFont.setBold(true);
} else {
headerFont.setFontHeightInPoints((short) 10);
}
cellStyle.setFont(headerFont);
return cellStyle;
}
3、私有方法setResponseHeader()是设置响应头和文件名。
// 设置返回的响应头
private static void setResponseHeader(HttpServletResponse response, String fileName) {
// 返回的文件名
String resName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)
+ "_" + DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMddHHmmss") + ".xls";
response.setContentType("application/vnd.ms-excel");
// excel的导出设置(文件名必须去掉空格,否则出现问题)
response.setHeader("Content-Disposition", "attachment;filename="
+ resName.replaceAll("\s*", StringUtils.EMPTY));
}
例子
1、首先定义一个Model类,在需要导出的字段上使用@ExcelResources注解,如下图:
参数clz的定义类
2、通过DB查询数据后,直接调用工具类导出Excel。
获取数据并调用实用方法
3、查看导出文件的内容
Excel文件内容
可扩展性
由于自定义注解,所以具有可扩展性,比如定义单元格的格式(字体、颜色、边框、类型等),增加单元格的勾选(单选、多选、范围等)。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 欧资源网