优化了下以前写的工具类。


优点:

  1. 标题支持从实体类中直接取值
  2. 数据列表支持List<自定义类/八大基本类型/String>
  3. 工具类采用可变参数,不会过多限制数据数量与数据格式

代码:

工具类:

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
public class MyExcelUtil {

    /**
     * 生成xlsx格式的Excel表格
     *
     * @param sheetName sheet名称
     * @param titleList 标题列表
     * @param data      数据列表,支持List<自定义类/八大基本类型/String>
     * @return XSSFWorkbook
     */
    public static XSSFWorkbook getXSSFWorkbook(String sheetName,
                                               List<String> titleList, List<?>... data) throws IllegalAccessException {
        // 创建HSSFWorkbook对象
        XSSFWorkbook wb = new XSSFWorkbook();
        // 创建sheet对象
        XSSFSheet sheet = wb.createSheet(sheetName);
        // 在sheet里创建第一行,这里即是表头
        XSSFRow rowTitle = sheet.createRow(0);

        // 写入表头的每一个列
        for (int i = 0; i < titleList.size(); i++) {
            // 创建单元格
            rowTitle.createCell(i).setCellValue(titleList.get(i));
        }

        // 写入每一行的记录
        int col = 1;
        for (List<?> dataList : data) {
            if (CollectionUtils.isEmpty(dataList)) {
                continue;
            }
            if (isBaseType(dataList.get(0))) {
                // List<八大基本类型/String>创建新的一行
                XSSFRow rowData = sheet.createRow(col++);
                for (int j = 0, le = dataList.size(); j < le; j++) {
                    //创建单元格
                    Object obj = dataList.get(j);
                    if (obj == null)
                        continue;
                    rowData.createCell(j).setCellValue(String.valueOf(obj));
                }
            } else {
                // List<自定义类>创建多行
                for (Object o : dataList) {
                    XSSFRow rowData = sheet.createRow(col++);
                    Class<?> cl = o.getClass();
                    Field[] fields = cl.getDeclaredFields();
                    for (int j = 0, le = fields.length; j < le; j++) {
                        // 设置字段可见,否则会报错,禁止访问
                        fields[j].setAccessible(true);
                        // 创建单元格
                        Object obj = fields[j].get(o);
                        if (obj == null) {
                            continue;
                        }
                        if (obj instanceof Date) {
                            rowData.createCell(j).setCellValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(obj));
                        } else {
                            rowData.createCell(j).setCellValue(String.valueOf(obj));
                        }
                    }
                }
            }
        }
        return wb;
    }

    /**
     * 用反射获取Desc注释值,来生成标题列表titleList
     *
     * @param titleClass
     * @param <T>
     * @return
     */
    public static <T> List<String> getTitleListByClass(Class<T> titleClass) {
        List<String> titleList = new ArrayList<>();
        Field[] titleFields = titleClass.getDeclaredFields();
        for (Field field : titleFields) {
            Desc desc = AnnotationUtils.findAnnotation(field, Desc.class);
            if (desc != null) {
                titleList.add(desc.value());
            }
        }
        return titleList;
    }

    /**
     * 将List<?>转换为List<T>
     *
     * @param obj
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> castList(Object obj, Class<T> clazz) {
        List<T> result = new ArrayList<>();
        if (obj instanceof List<?>) {
            for (Object o : (List<?>) obj) {
                result.add(clazz.cast(o));
            }
            return result;
        }
        return null;
    }

    /**
     * 判断object是否为基本类型或String
     *
     * @param object
     * @return
     */
    public static boolean isBaseType(Object object) {
        Class<?> className = object.getClass();
        return (className.equals(java.lang.Integer.class) ||
                className.equals(java.lang.Byte.class) ||
                className.equals(java.lang.Long.class) ||
                className.equals(java.lang.Double.class) ||
                className.equals(java.lang.Float.class) ||
                className.equals(java.lang.Character.class) ||
                className.equals(java.lang.Short.class) ||
                className.equals(java.lang.Boolean.class) ||
                className.equals(java.lang.String.class));
    }
}

注释类:

@Retention(RetentionPolicy.RUNTIME)
public @interface Desc {
    String value();
}

自义定实体类例子:

@Data
public class ExportResp {

    @Desc("编号")
    private Integer id;

    @Desc("标题")
    private String title;

    @Desc("状态")
    private String status;
}

接口例子:

@GetMapping("/getExcel")
    public void getExcel(HttpServletResponse response) {
        // 随便造几个数据
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
        List<Double> doubles = Arrays.asList(1.12, 2.23, 3.34, 4.45, 5.56);
        List<String> strings = Arrays.asList("测试1", "测试2", "测试3", "测试4", "测试5");
        ExportResp resp = new ExportResp();
        resp.setId(1);
        resp.setTitle("标题优秀");
        resp.setStatus("状态优秀");
        List<ExportResp> respList = new ArrayList<>();
        respList.add(resp);
        respList.add(resp);

        // 使用 try-with-resources 使代码更简洁,编译器会自动补全close()
        try (ServletOutputStream out = response.getOutputStream()) {
            String fileName = URLEncoder.encode("默认标题.xlsx", "UTF-8");
            response.reset();
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            // filename*=utf-8''支持中文标题
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ";" + "filename*=utf-8''" + fileName);
            MyExcelUtil.getXSSFWorkbook("bug记录", MyExcelUtil.getTitleListByClass(ExportResp.class), integers, doubles, strings, respList).write(out);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

调用接口:

http://localhost:8080/excel/getExcel

相关文章: