最近工作中需要导出excel。这次机智一点做个笔记,顺便写了一个比较通用的工具类。自然目前不能生成java实体类属性嵌套多次的这种没办法导出了,后续有需要的时候我再改改。
首先,java后端导出excel需要的HSSFWorkbook或者XSSFWorkbook,实际上他们并没有什么区别,只是导出的格式不同而已(在我看来)。前者导出.xls格式,后者导出.xlsx格式的。不过新版的好像就没有这个区别了吧,因为我也是使用HSSFWorkbook,但是同样也能导出.xlsx格式的文件呦。
HSSFWorkbook(XSSFWorkbook)他们都是Apache的产品,你可以按照需求随意切换!他们没有实质上的太多的差别,听别人说他们还有一个差别就是HSSFWorkbook只能导出65535条数据,再大的话就会报错,而XSSFWorkbook可以导出数据量是非常大的(在下并没有测试,感兴趣的兄弟可以试一下),实际上一般我们也用不了导出超过65535条数据吧。
先看一下api吧!http://poi.apache.org/apidocs/dev/org/apache/poi/hssf/usermodel/HSSFWorkbook.html
接下来我就直接粘代码了
package com.ml.code.one.until; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.VerticalAlignment; import org.apache.poi.ss.util.CellRangeAddress; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * @author lhf * @param <T> * @since 1.0 * <p> * 导出excel工具类,比较通用的呦 * </p> */ public class ExcelUtil<T> { /** * 导出excel * @param datas 要导出的数据 * @param excelName excel名字 * @param sheetHead 表头 key对应java中的属性名,每个key前边 * 加01-99,为了排序。value对应表头的中文名 * @param path 路径 * @param entity 分装属性名(如果有连表查询的话) * @return -1其他异常 1导出成功 2目标文件被占用(文件已存在但是被其他应用占用) 0 导出失败(io) * @throws IllegalAccessException * @throws InvocationTargetException */ public int outExcel(Collection<T>datas, String excelName, Map<String, String> sheetHead,String path,String... entity) throws IllegalAccessException, InvocationTargetException { HSSFWorkbook workbook = new HSSFWorkbook(); //excel对象 HSSFSheet sheet = workbook.createSheet(excelName); sheet.autoSizeColumn(1,true); //第一行表名 HSSFRow row = sheet.createRow(0); HSSFCell cell = row.createCell(0); cell.setCellValue(excelName); //获取表头集合中所有的key(那么顺序可能会乱) Set<String> sheetKeySet = sheetHead.keySet(); //转换为list集合,并排序 List<String> sheetKey = sheetKeySet.stream().sorted().collect(Collectors.toList());//将set集合进行排序并转换成List集合 //合并单元格 sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, sheetKey.size() - 1)); //设置单元格文字垂直居中,水平居中 HSSFCellStyle cellStyle = workbook.createCellStyle(); cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); cellStyle.setAlignment(HorizontalAlignment.CENTER_SELECTION); cell.setCellStyle(cellStyle); //日期格式 HSSFCellStyle style = workbook.createCellStyle(); // HSSFDataFormat dataFormat = workbook.getCreationHelper().createDataFormat(); HSSFDataFormat dataFormat = workbook.createDataFormat(); style.setDataFormat(dataFormat.getFormat("yyyy/m/d")); //表头 HSSFRow row1 = sheet.createRow(1); for (int i = 0; i < sheetKey.size(); i++) { HSSFCell cell1 = row1.createCell(i); cell1.setCellValue(sheetHead.get(sheetKey.get(i))); cell1.setCellStyle(cellStyle); } List<T> data = datas.stream().collect(Collectors.toList()); //将Collection转为List集合 T t=null; //获取到的泛型 //开始向excel表中添加信息 for (int i = 0; i < data.size(); i++) {//生成行 HSSFRow row2 = sheet.createRow(i + 2); t=data.get(i); Method[] declaredMethods = t.getClass().getDeclaredMethods(); for (int m = 0; m < sheetKey.size(); m++) {//拿到key值 key的数量代表列的数量 String key = sheetKey.get(m).substring(2); String newKey = "get" + key.substring(0, 1).toUpperCase() + key.substring(1);//key首字母大写 for (int k = 0; k < declaredMethods.length; k++) {//遍历字段进行比较并添加信息//添加列信息 String methodName = declaredMethods[k].getName();//方法名 Object invoke1=null; if (methodName.equals(newKey)) {//执行方法获取属性值 invoke1= declaredMethods[k].invoke(t); }else if (entity.length>0){//如果有分装属性 A类中封装了B类 一下代码块就是执行B类中get方法 for (int j=0;j<entity.length;j++){ String s = "get"+entity[j].substring(0,1).toUpperCase()+entity[j].substring(1); if (s.equals(methodName)){ Object oo = declaredMethods[k].invoke(t); Method[] mes = oo.getClass().getDeclaredMethods(); for (int p=0;p<mes.length;p++){ if (mes[p].getName().equals(newKey)){ invoke1 = mes[p].invoke(oo); } } } } } if (invoke1 instanceof String){ boolean matches = Pattern.matches("[0-9]*", invoke1.toString()); if (matches){ Double invoke = (Double)invoke1; row2.createCell(m).setCellValue(invoke); }else { String invoke= (String) invoke1; row2.createCell(m).setCellValue(invoke); } } else if (invoke1 instanceof Integer){ Integer invoke = (Integer) invoke1; row2.createCell(m).setCellValue(invoke); } else if (invoke1 instanceof Short){ Short invoke = (Short) invoke1; row2.createCell(m).setCellValue(invoke); } else if (invoke1 instanceof Long){ Long invoke = (Long)invoke1; row2.createCell(m).setCellValue(invoke); } else if (invoke1 instanceof Boolean){ Boolean invoke = (Boolean)invoke1; row2.createCell(m).setCellValue(invoke); } else if (invoke1 instanceof Double){ Double invoke = (Double)invoke1; row2.createCell(m).setCellValue(invoke); } else if (invoke1 instanceof Float){ Float invoke = (Float)invoke1; row2.createCell(m).setCellValue(invoke); } else if (invoke1 instanceof Byte){ Byte invoke = (Byte)invoke1; row2.createCell(m).setCellValue(invoke); } else if (invoke1 instanceof Character){ Character invoke = (Character)invoke1; row2.createCell(m).setCellValue(invoke); } else if (invoke1 instanceof Date){ Date invoke = (Date)invoke1; HSSFCell cell1 = row2.createCell(m); cell1.setCellStyle(style); cell1.setCellValue(invoke); } else if (invoke1 instanceof java.sql.Date){ java.sql.Date invoke = (java.sql.Date)invoke1; HSSFCell cell1 = row2.createCell(m); cell1.setCellStyle(style); cell1.setCellValue(invoke); } //if 类型转换 }//for k }//for m }//for i //写入到文件 try { File file = new File(path + excelName + ".xlsx"); if (!file.exists()) { file.createNewFile(); } workbook.write(file); return 1;//导出成功 } catch (FileNotFoundException e){ e.printStackTrace(); return 2;//目标文件被占用 } catch (IOException e) { e.printStackTrace(); return 0;//导出失败 } catch (Exception e){ e.printStackTrace(); return -1;//其他异常 }finally { try { if (workbook!=null) workbook.close(); } catch (IOException e) { e.printStackTrace(); } } } }
代码很简单,相信大家看的懂。有什么不明白的加我qq1490030544。
以下的伪代码是表名什么情况下不能用该类:
以上这种情况通过A类导出Excel时可以拿到B类的id,但是拿不到C类的id(虽然可以拿到C类的对象),这个后续可能我会改造一下,看时间安排吧