Java POI导出Excel

本文介绍了一种Excel导出功能的优化方案,通过XML配置文件和反射机制实现了表头和内容的灵活填充,大大提高了开发效率。

最近在项目中,需要做一个Excel导出功能,当时由于时间比较紧,忙于交互,因此草草的把功能实现就完了,做的比较Lower,我们都知道,Excel导出,一般都会有表头和内容两部分,开始做的时候,表头是使用数组的形式写死,内容部分,笨办法一个一个填充每个单元格的内容,当时的代码是这样的

// 这是定义表头的数组
private final String[] titles = { "发货人", "联系方式", "提货时间", "发货地址", "详细地址","目的地", "目的仓", "交仓平台", "件数(PCS)", "重量(KG)", "体积(m3)", "是否装卸", "备注" };

// 表头填充内容
for (int i = 0; i < titles.length; i++) {
    cell = headRow.createCell(i);
    cell.setCellStyle(headStyle);
    cell.setCellValue(titles[i]);
}

而内容部分是这样子的

for (int i = 0; i < orderList.size(); i++) {
    // 自动设置列宽
    bodyRow = sheet.createRow(i + 1);
    // 发货人
    cell = bodyRow.createCell(0);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getConsignor());

    // 联系方式
    cell = bodyRow.createCell(1);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getPhone());
    // 提货时间
    cell = bodyRow.createCell(2);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getDeliveryDate());
    // 发货地址
    cell = bodyRow.createCell(3);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getFromProvinceName()
            + orderList.get(i).getFromCityName()
            + orderList.get(i).getFromAreaName());
    // 详细地址
    cell = bodyRow.createCell(4);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getFromDetailAddress());

    // 目的地
    cell = bodyRow.createCell(5);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getToProvinceName()
            + orderList.get(i).getToCityName()
            + orderList.get(i).getToAreaName());
    // 目的仓
    cell = bodyRow.createCell(6);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getToWarehouse());

    // 交仓平台
    cell = bodyRow.createCell(7);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getDeliveryPlatform());

    // 件数
    cell = bodyRow.createCell(8);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getNumber());

    // 重量
    cell = bodyRow.createCell(9);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getWeight());

    // 体积
    cell = bodyRow.createCell(10);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getVolume());

    // 是否装卸
    cell = bodyRow.createCell(11);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getLoadFlag());

    // 备注
    cell = bodyRow.createCell(12);
    cell.setCellStyle(bodyStyle);
    cell.setCellValue(orderList.get(i).getComments());
}

内容部分的每个单元格都是一个一个创建的,如果有100列,那就要从0写到99,是不是感觉很Lower啊,实在是忍受不了,决定对这块代码进行重构,想了另一个实现方式,我的思路是:
1、提供一个xml的配置文件,xml中定义好Sheet页名称,对应的实体对象,需要导出的列,列名称,及每一列对应的实体对象属性
2、解析XML,封装列头
3、通过反射,执行列对应属性的get方法,得到每一列的内容
4、以流的形式输出给用户,生成Excel

下面来看具体的实现
1、在resources/excel资源文件目录下新建xuanju.export.xml配置文件,内容如下

<?xml version="1.0" encoding="UTF-8"?>
<export>
    <sheet id="exportOrderList" name="order" class="com.xuanju.entity.order.Order">
        <column title="发布人" name="Consignor"></column>
        <column title="联系方式" name="Phone"></column>
        <column title="发货时间" name="DeliveryDate"></column>
        <column title="发货地址" name="FromProvinceCode"></column>
    </sheet>
</export>

sheet:一个sheet页
id:sheet页ID,一定要唯一,标识按哪个模板导出
name:Sheet页的名称
class:对应的实体对象

column:导出的列
title:列头的名称
name:当前列需要以哪个实体对象的属性填充,例如name=”Consignor”,表进当前列以Order.getConsignor()的值填充,注意name值首字母一定要大写,反射执行get方法时会用到

2、增加一个导出的工具类ExportExcelUtil,在类中提供导出的方法

public static <T> void exportExcel(List<T> list, String sheetId,
        HttpServletResponse response) throws Exception {
    try {
        // 根据sheetId到xuanju.export.xml文件中找,需要按哪个模块导出,并返回对应的DOM树
        Element element = readXML(sheetId);
        List<Element> columns = element.elements();
        XSSFWorkbook wb = new XSSFWorkbook();
        // 创建一个Sheet页
        XSSFSheet sheet = wb.createSheet(element.attributeValue(("name")));
        // 创建表头
        createTitleRow(wb, sheet, columns);
        // 创建表体
        createContentRow(list, sheet, columns,element.attributeValue("class"));
        // 输出
        outputString(wb, response);
    } catch (Exception e) {
        throw new Exception("Export excel error.");
    }
}

// 根据ID找到对应的导出模板,并返回对应的DOM树
private static Element readXML(String sheetId) throws DocumentException {
    String path = "/Users/longwentao/java/svn_code/xuanju/trunk/Code/xuanju.web/src/main/resources/excel/xuanju.export.xml";
    SAXReader reader = new SAXReader();
    Document document = reader.read(new File(path));
    Element root = document.getRootElement();
    Iterator<Element> iterator = root.elementIterator();
    Element element = null;
    while (iterator.hasNext()) {
        element = iterator.next();
        if (sheetId.equals(element.attributeValue("id"))) {
            break;
        }
    }
    return element;
}
// 根据column创建表头,并根据title设置表头的名称
private static void createTitleRow(XSSFWorkbook wb, XSSFSheet sheet,
        List<Element> columns) {
    // 创建表头
    XSSFRow headRow = sheet.createRow(0);
    XSSFCellStyle headStyle = getHeadStyle(wb);
    Cell cell = null;
    for (int i = 0; i < columns.size(); i++) {
        cell = headRow.createCell(i);
        cell.setCellStyle(headStyle);
        cell.setCellValue(columns.get(i).attributeValue("title"));
    }
}
/**
 * 创建内容行
 * @param list 数据实体列表
 * @param sheet Sheet页
 * @param columns 导出的列
 * @param clazzString 配置文件中Sheet页上的class值
 * @throws Exception
 */
private static <T> void createContentRow(List<T> list, XSSFSheet sheet,
        List<Element> columns, String clazzString) throws Exception {
    // 反射创建对象实例
    Class<? extends Object> clazz = Class.forName(clazzString);
    Row contentRow = null;
    Cell contentCell = null;
    Method method = null;
    String cellValue = null;
    Object obj = null;
    // 根据list确认行数
    for (int i = 0; i < list.size(); i++) {
        contentRow = sheet.createRow(i + 1);
        // 根据column确认列
        for (int j = 0; j < columns.size(); j++) {
            // 使用反射执行当前列对应实体属性的get方法获得值,并填充到对应的单元格中
            method = clazz.getMethod("get"+ columns.get(j).attributeValue("name"));
            obj = method.invoke(list.get(i));
            if (!CommonUtils.isNull(obj)) {
                cellValue = obj.toString();
            }
            contentCell = contentRow.createCell(j);
            contentCell.setCellValue(cellValue);
        }
    }
}
// 导出
private static void outputString(XSSFWorkbook wb,
        HttpServletResponse response) throws IOException {
    BufferedOutputStream bufferedOutPut = null;
    try {
        setResponseAttr(response);
        bufferedOutPut = new BufferedOutputStream(
                response.getOutputStream());
        bufferedOutPut.flush();
        wb.write(bufferedOutPut);
        bufferedOutPut.close();
    } catch (Exception e) {

    } finally {
        if (CommonUtils.isNull(bufferedOutPut)) {
            bufferedOutPut.close();
        }
    }
}
// 设置导出的一些属性
private static void setResponseAttr(HttpServletResponse response) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmssms");
    String dateStr = sdf.format(new Date());
    // 指定下载的文件名
    response.setHeader("Content-Disposition", "attachment;filename="
            + dateStr + ".xlsx");
    response.setContentType("application/vnd.ms-excel;charset=UTF-8");
    response.setHeader("Pragma", "no-cache");
    response.setHeader("Cache-Control", "no-cache");
    response.setDateHeader("Expires", 0);
}
// 表头样式
private static XSSFCellStyle getHeadStyle(XSSFWorkbook wb) {
    // 创建单元格样式
    XSSFCellStyle cellStyle = wb.createCellStyle();
    // 设置单元格的背景颜色为淡蓝色
    cellStyle.setFillForegroundColor(HSSFColor.PALE_BLUE.index);
    cellStyle.setFillPattern(XSSFCellStyle.SOLID_FOREGROUND);
    // 设置单元格居中对齐
    cellStyle.setAlignment(XSSFCellStyle.ALIGN_CENTER);
    // 设置单元格垂直居中对齐
    cellStyle.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);
    // 创建单元格内容显示不下时自动换行
    cellStyle.setWrapText(true);
    // 设置单元格字体样式
    XSSFFont font = wb.createFont();
    // 设置字体加粗
    font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);
    font.setFontName("宋体");
    font.setFontHeight((short) 200);
    cellStyle.setFont(font);
    // 设置单元格边框为细线条
    cellStyle.setBorderLeft(XSSFCellStyle.BORDER_THIN);
    cellStyle.setBorderBottom(XSSFCellStyle.BORDER_THIN);
    cellStyle.setBorderRight(XSSFCellStyle.BORDER_THIN);
    cellStyle.setBorderTop(XSSFCellStyle.BORDER_THIN);
    return cellStyle;
}

3、在Controller层中,直接调用上面的导出接口就可以了

@Autowired
private IOrderService orderService;

List<Order> orderList = orderService.queryOrderList(null);
ExportExcelUtil.exportExcel(orderList, "exportOrderList", response);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值