一, 背景
当前 B/S 模式已成为应用开发的主流, 而企业办公系统中, 常常有客户这样子要求: 你要把我们的报表直接用 Excel 打开 (电信系统, 银行系统), 或者是: 我们已经习惯用 Execel 打印, 这样我们实际的开发中, 很多时候需要实现导入, 导出 Excel 应用.
最近在 java 上做了一个人 Excel 的导出功能, 写了一个通用类, 在这里分享给大家. 该类支持多 sheet, 且无需动手进行复杂的类型转换, 只需提供三个参数即可:
fileName
Excel 的文件名
HashMap<String,List<?>> data
具体数据, 每个 List 代表一张表的数据,? 表示可以为任意类型的自定义对象
LinkedHashMap<String,String[ ] [ ]> headers
String 代表 sheet 名, 每个 String[ ] [ ] 代表一个 sheet 的定义, 举个列子如下:
- String[][] headers = {
- {"field1","参数 1"}
- ,{"field2","参数 2"}
- ,{"field3","参数 3"}
- }
其中的 field1,field2,field3 为对象中的属性名, 参数 1, 参数 2, 参数 3 为列名, 实际上这个指定了列的名称和这个列用到数据对象的哪个属性.
二, 怎么用
以一个例子来说明怎么用, 假设有两个类 A 和 B 定义如下:
- public class A{
- private String name;
- privaite String address;
- }
- public class B{
- private int id;
- private double sum;
- private String cat;
- }
现在我们通过查询数据库获得了 A 和 B 的两个列表:
- List<A> dataA = ......;
- List<B> dataB = ......;
我们将这两个导出到 Excel 中, 首先需要定义 sheet
- String [][] sheeetA ={
- {"name","姓名"}
- ,{"address","住址"}
- }
- String [][] sheetB ={
- {"id","ID"},
- {"sum","余额"},
- {"cat","猫的名字"}
- }
然后将数据汇总构成一个 ExcelUtil
- // 第一个参数: Excel 的名字
- String fileName = "测试 Excel";
- // 第二个参数: sheet 的具体数据
- HashMap<String,List<?>> data = new HashMap<>();
- //ASheet 为表名, 后面 headers 里的 key 要跟这里一致
- data.put("ASheet",dataA);
- data.put("BSheet",dataB);
- LinkedHashMap<String,String[][]> headers = new LinkedHashMap<>();
- headers.put("ASheet",sheetA);
- headers.put("BSheet",sheetB);
- ExcelUtil excelUtil = new ExcelUtil(fileName,data,headers);
- // 获取表格对象
- HSSFWorkbook workbook = excelUtil.createExcel();
- // 这里内置了一个写到 response 的方法 (判断浏览器类型设置合适的参数), 如果想写到文件也是类似的
- workbook.writeToResponse(workbook,request,response);
三, 实现原理
1. 遍历 headers 创建 sheet
- public HSSFWorkbook createExcel() throws Exception {
- try {
- // 创建工作簿
- HSSFWorkbook workbook = new HSSFWorkbook();
- // 遍历 headers 创建表格
- for (String key : headers.keySet()) {
- this.createSheet(workbook, key, headers.get(key), this.data.get(key));
- }
- return workbook;
- } catch (Exception e) {
- log.error("创建表格失败:{}", e.getMessage());
- throw e;
- }
- }
将 workbook,sheet 名, 表头数据, 行数据传入 createSheet 方法中创建 sheet
2. 创建表头
表头也就是一个表格的第一行, 通常用来对列进行说明
- // 创建一个工作表
- HSSFSheet sheet = workbook.createSheet(sheetName);
- // 列数
- int cellNum = header.length;
- // 单元行, 单元格
- HSSFRow row;
- HSSFCell cell;
- // 表头单元格样式
- HSSFCellStyle columnTopStyle = this.getColumnTopStyle(workbook);
- // 设置表头
- row = sheet.createRow(0);
- for (int i = 0; i <cellNum; i++) {
- cell = row.createCell(i);
- cell.setCellStyle(columnTopStyle);
- String str = header[i][1];
- cell.setCellValue(str);
- // 设置列宽为表头的文字宽度 + 6 个半角符号宽度
- sheet.setColumnWidth(i, (str.getBytes("utf-8").length + 6) * 256);
- }
插入数据
这里是我们最重要的部分, 首先通过数据的类对象获取它的反射属性 Field 类, 然后将属性名和 Field 做一个 hash 映射, 避免循环查找, 提高插入速度, 接着通过一个 switch 语句, 根据属性类别设置, 主要代码如下:
- /**
- * 设置单元格, 根据 fieldName 获取对应的 Field 类, 使用反射得到值
- *
- * @param cell 单元格实例
- * @param obj 存有属性的对象实例
- * @param fieldMap 属性名与 Field 的映射
- * @param fieldName 属性名
- */
- private void setCell(HSSFCell cell, Object obj, Map<String, Field> fieldMap, String fieldName) throws Exception {
- // 获取该属性的 Field 对象
- Field field = fieldMap.get(fieldName);
- // 通过反射获取属性的值, 由于不能确定该值的类型, 用下面的判断语句进行合适的转型
- Object value = field.get(obj);
- if (value == null) {
- cell.setCellValue("");
- } else {
- switch (field.getGenericType().getTypeName()) {
- case "java.lang.String":
- cell.setCellValue((String) value);
- break;
- case "java.lang.Integer":
- case "int":
- cell.setCellValue((int) value);
- break;
- case "java.lang.Double":
- case "double":
- cell.setCellValue((double) value);
- break;
- case "java.util.Date":
- cell.setCellValue(this.dateFormat.format((Date) value));
- break;
- default:
- cell.setCellValue(obj.toString());
- }
- }
- }
来源: http://www.bubuko.com/infodetail-3478639.html