package cn.exrick.xboot.core.common.utils; import cn.hutool.core.util.StrUtil; import jxl.Cell; import jxl.Sheet; import jxl.Workbook; import jxl.read.biff.BiffException; import jxl.write.Label; import jxl.write.WritableSheet; import jxl.write.WritableWorkbook; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.*; import java.util.Map.Entry; /** * 通用的excel导入导出工具,由于时间仓促所以这些代码中有许多不尽人意的地方。 * 现在只能说是实现的功能。后期应该对逻辑和方法代码进行细致的优化。 */ // FIXME: 2017/9/12 这个类的代码自认为写的很烂,先凑活用吧!!!!!!!!!! public final class ExcelUtil { private ExcelUtil() { } /** * function 用于实现将后台数据导入到Excel中 * * @param * 模板中的类型 * @param list * 装在数据的list * @param fieldMap * Excel中的数据容器,!!值得注意的是fileMap中的key要和表格中的表头的英文名数据一一对应才行 * 如果需要的是引用对象的某个属性,则英文属性使用的是类似于EL表达式的格式 * 如我们存储的是student,但是在student中要存储班级名称,班级是个对象,这个时候我们就可以这样实现 * fileMap.put("Calzz.name","学生所在班级"); * @param sheetName * Excel中工作表的名称 * @param sheetSize * 工作也得数量 * @param out * 字节流输出流 * @throws RuntimeException * 1--数据源没有数据 */ public static void listToExcel(List list, LinkedHashMap fieldMap, String sheetName, int sheetSize, OutputStream out) throws RuntimeException { // 数据为空或者是数据不存在 if (list == null || list.size() == 0) { throw new RuntimeException("数据源中没有任何数据!请核实"); } // 一次性处理的表格sheet的范围 if (sheetSize > 65535 || sheetSize < 1) { // 不符合规范 sheetSize = 65535; } // 创建工作部, 并通过out发送 WritableWorkbook wwb = null; try { wwb = Workbook.createWorkbook(out);// 这里的workBook包是jxl里面的 // 计算一共有多少个工作表 double sheetNum = Math.ceil(list.size() / new Integer(sheetSize).doubleValue()); // 创建相应的工作表,并向其中填充数据 for (int i = 0; i < sheetNum; i++) { // 判断如果只有一张工作表的情况 if (sheetNum == 1) { WritableSheet sheet = wwb.createSheet(sheetName, i);// 以下表来命名 fillSheet(sheet, list, fieldMap, 0, list.size() - 1); } else { WritableSheet sheet = wwb.createSheet(sheetName + (i + 1), i); // 获取开始索引和结束索引 int firstIndex = i * sheetSize; int lastIndex = (i + 1) * sheetSize - 1 > list.size() - 1 ? list.size() - 1 : (i + 1) * sheetSize - 1; // 填充工作表 fillSheet(sheet, list, fieldMap, firstIndex, lastIndex); } } wwb.write(); wwb.close(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); // 如果是ExcelExpection直接抛出 if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new RuntimeException("Excel导入失败"); } } } /** * 重载的函数 */ public static void listToExcel(List list, LinkedHashMap fieldMap, String sheetName, OutputStream out) throws RuntimeException { listToExcel(list, fieldMap, sheetName, 65535, out); } /** * @MethodName : listToExcel * @Description : 导出Excel(导出到浏览器,可以自定义工作表的大小) * @param list * 数据源 * @param fieldMap * 类的英文属性和Excel中的中文列名的对应关系 * @param sheetSize * 每个工作表中记录的最大个数 * @param response * 使用response可以导出到浏览器 * @throws RuntimeException */ public static void listToExcel(List list, LinkedHashMap fieldMap, String sheetName, int sheetSize, HttpServletResponse response) throws RuntimeException { // 设置默认文件名为当前时间:年月日时分秒 String fileName = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date()).toString(); // 设置response头信息 response.reset(); response.setContentType("application/vnd.ms-excel"); // 改成输出excel文件 response.setHeader("Content-disposition", "attachment; filename=" + fileName + ".xls"); // 创建工作簿并发送到浏览器 try { OutputStream out = response.getOutputStream(); listToExcel(list, fieldMap, sheetName, sheetSize, out); } catch (Exception e) { e.printStackTrace(); // 如果是RuntimeException,则直接抛出 if (e instanceof RuntimeException) { throw (RuntimeException) e; // 否则将其它异常包装成RuntimeException再抛出 } else { throw new RuntimeException("导出Excel失败"); } } } /** * @MethodName : listToExcel * @Description : 导出Excel(导出到浏览器,工作表的大小是2003支持的最大值) * @param list * 数据源 * @param fieldMap * 类的英文属性和Excel中的中文列名的对应关系 * @param response * 使用response可以导出到浏览器 * @throws RuntimeException */ public static void listToExcel(List list, LinkedHashMap fieldMap, String sheetName, HttpServletResponse response) throws RuntimeException { listToExcel(list, fieldMap, sheetName, 65535, response); } /** * * @param * 模型T * @param in * Excel的输入流 * @param sheetName * Excel文件名 * @param entityClass * 实体对象 * @param fieldMap * Excel中的中文猎头和类的英文属性的对应关系MAP * @param uniqueFields * 指定业务主键组合(既复合主键) , 这些列的组合不能重复 * @return * @throws RuntimeException */ public static List excelToList(InputStream in, String sheetName, Class entityClass, LinkedHashMap fieldMap, String[] uniqueFields) throws RuntimeException { // 定义要返回的list List resultList = new ArrayList(); try { // 根据Excel数据源来创建WorkBook Workbook wb = Workbook.getWorkbook(in); // 获取工作表 Sheet sheet = null; if(StrUtil.isNotBlank(sheetName)){ sheet = wb.getSheet(sheetName); }else{ sheet = wb.getSheets()[0]; } // 获取工作表的有效行数 默认为0 int realRows = 0; for (int i = 0; i < sheet.getRows(); i++) { int nullCols = 0; for (int j = 0; j < sheet.getColumns(); j++) { // 获取单元格内容 Cell currentCell = sheet.getCell(j, i); if (currentCell == null || "".equals(currentCell.getContents().toString())) { nullCols++; } } if (nullCols == sheet.getColumns()) { break; } else { realRows++; } } if (realRows <= 1) { // 如果Excel中没有数据则提示报错 throw new RuntimeException("Excel中没有数据"); } Cell[] firstRow = sheet.getRow(0); String[] excelFieldNames = new String[firstRow.length]; // 获取Excel中的列名 for (int i = 0; i < firstRow.length; i++) { excelFieldNames[i] = firstRow[i].getContents().toString().trim(); } // 判断需要的字段是否否已经在Excel中存在了 boolean isExist = true; // 将字段名数组转变成list集合 List excelFieldList = Arrays.asList(excelFieldNames); for (String cnName : fieldMap.values()) { if (!excelFieldList.contains(cnName)) { isExist = false; break; } } // 如果有列名不匹配 报错 if (!isExist) { throw new RuntimeException("Excel中缺少必要的字段,或字段名称有误!"); } // 将列名和列号放入map中,这样方便我们通过列名拿到列号 LinkedHashMap colMap = new LinkedHashMap(); for (int i = 0; i < excelFieldNames.length; i++) { colMap.put(excelFieldNames[i], firstRow[i].getColumn()); } if (uniqueFields != null && uniqueFields.length > 0) { // 判断是否有重复行 // 1.获取uniqueFields指定的列 Cell[][] uniqueCells = new Cell[uniqueFields.length][]; for (int i = 0; i < uniqueFields.length; i++) { int col = colMap.get(uniqueFields[i]); uniqueCells[i] = sheet.getColumn(col); } // 2.从指定列中寻找重复行 for (int i = 1; i < realRows; i++) { int nullCols = 0; for (int j = 0; j < uniqueFields.length; j++) { String currentContent = uniqueCells[j][i].getContents(); Cell sameCell = sheet.findCell(currentContent); /* * Cell sameCell = sheet.findCell(currentContent, * uniqueCells[j][i].getColumn(), uniqueCells[j][i] * .getRow() + 1, uniqueCells[j][i] .getColumn(), * uniqueCells[j][realRows - 1] .getRow(), true); */ if (sameCell != null) { nullCols++; } } if (nullCols == uniqueFields.length) { throw new RuntimeException("Excel中有重复行,请检查"); } } } // 将sheet转换为list for (int i = 1; i < realRows; i++) { // 新建要转换的对象 T entity = entityClass.newInstance(); // 给对象中的字段赋值 for (Entry entry : fieldMap.entrySet()) { // 获取中文字段名 String cnNormalName = entry.getValue(); // 获取英文字段名 String enNormalName = entry.getKey(); // 根据中文字段名获取列 int col = colMap.get(cnNormalName); // 获取当前单元格的内容 去空格 String content = sheet.getCell(col, i).getContents().toString().trim(); // 给对象赋值 if(content!=null && !"".equals(content) && content.length()>0){ setFieldValueByName(enNormalName, content, entity); } } resultList.add(entity); } } catch (BiffException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("导入Excel失败"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("导入Excel失败"); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("导入Excel失败"); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("导入Excel失败"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("导入Excel失败"); } return resultList; } /* 辅助方法 */ /** * @MethodName : getFieldValueByName * @Description : 根据字段名获取字段值 * @param fieldName * 字段名 * @param o * 对象 * @return 字段值 */ private static Object getFieldValueByName(String fieldName, Object o) throws Exception { Object value = null; Field field = getFieldByName(fieldName, o.getClass()); if (field != null) { field.setAccessible(true); value = field.get(o); } else { throw new RuntimeException(o.getClass().getSimpleName() + "类不存在字段名 " + fieldName); } return value; } /** * @MethodName : getFieldByName * @Description : 根据字段名获取字段 * @param fieldName * 字段名 * @param clazz * 包含该字段的类 * @return 字段 */ private static Field getFieldByName(String fieldName, Class clazz) { // 拿到本类的所有字段 Field[] selfFields = clazz.getDeclaredFields(); // 如果本类中存在该字段,则返回 for (Field field : selfFields) { if (field.getName().equals(fieldName)) { return field; } } // 否则,查看父类中是否存在此字段,如果有则返回 Class superClazz = clazz.getSuperclass(); if (superClazz != null && superClazz != Object.class) { return getFieldByName(fieldName, superClazz); } // 如果本类和父类都没有,则返回空 return null; } /** * @MethodName : getFieldValueByNameSequence * @Description : 根据带路径或不带路径的属性名获取属性值 * 即接受简单属性名,如userName等,又接受带路径的属性名,如student.department.name等 * * @param fieldNameSequence * 带路径的属性名或简单属性名 * @param o * 对象 * @return 属性值 * @throws Exception */ private static Object getFieldValueByNameSequence(String fieldNameSequence, Object o) throws Exception { Object value = null; // 将fieldNameSequence进行拆分 String[] attributes = fieldNameSequence.split("\\."); if (attributes.length == 1) { value = getFieldValueByName(fieldNameSequence, o); } else { // 根据属性名获取属性对象 Object fieldObj = getFieldValueByName(attributes[0], o); String subFieldNameSequence = fieldNameSequence.substring(fieldNameSequence.indexOf(".") + 1); value = getFieldValueByNameSequence(subFieldNameSequence, fieldObj); } return value; } /** * @MethodName : setFieldValueByName * @Description : 根据字段名给对象的字段赋值 * @param fieldName * 字段名 * @param fieldValue * 字段值 * @param o * 对象 */ private static void setFieldValueByName(String fieldName, Object fieldValue, Object o) throws Exception { Field field = getFieldByName(fieldName, o.getClass()); if (field != null) { field.setAccessible(true); // 获取字段类型 Class fieldType = field.getType(); // 根据字段类型给字段赋值 if (String.class == fieldType) { field.set(o, String.valueOf(fieldValue)); } else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) { field.set(o, Integer.parseInt(fieldValue.toString())); } else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) { field.set(o, Long.valueOf(fieldValue.toString())); } else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) { field.set(o, Float.valueOf(fieldValue.toString())); } else if ((Short.TYPE == fieldType) || (Short.class == fieldType)) { field.set(o, Short.valueOf(fieldValue.toString())); } else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) { field.set(o, Double.valueOf(fieldValue.toString())); } else if (Character.TYPE == fieldType) { field.set(o, Character.valueOf(fieldValue.toString().charAt(0))); } else if (Date.class == fieldType) { String dateStr = fieldValue.toString(); if(dateStr.length() > 10){ field.set(o, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateStr)); }else{ field.set(o, new SimpleDateFormat("yyyy-MM-dd").parse(dateStr)); } } else { field.set(o, fieldValue); } } else { throw new RuntimeException(o.getClass().getSimpleName() + "类不存在字段名 " + fieldName); } } /** * @MethodName : setColumnAutoSize * @Description : 设置工作表自动列宽和首行加粗 * @param ws */ private static void setColumnAutoSize(WritableSheet ws, int extraWith) { // 获取本列的最宽单元格的宽度 for (int i = 0; i < ws.getColumns(); i++) { int colWith = 0; for (int j = 0; j < ws.getRows(); j++) { String content = ws.getCell(i, j).getContents().toString(); int cellWith = content.length(); if (colWith < cellWith) { colWith = cellWith; } } // 设置单元格的宽度为最宽宽度+额外宽度 ws.setColumnView(i, colWith + extraWith); } } /** * @MethodName : fillSheet * @Description : 向工作表中填充数据 * @param sheet * 工作表 * @param list * 数据源 * @param fieldMap * 中英文字段对应关系的Map * @param firstIndex * 开始索引 * @param lastIndex * 结束索引 */ private static void fillSheet(WritableSheet sheet, List list, LinkedHashMap fieldMap, int firstIndex, int lastIndex) throws Exception { // 定义存放英文字段名和中文字段名的数组 String[] enFields = new String[fieldMap.size()]; String[] cnFields = new String[fieldMap.size()]; // 填充数组 int count = 0; for (Entry entry : fieldMap.entrySet()) { enFields[count] = entry.getKey(); cnFields[count] = entry.getValue(); count++; } // 填充表头 for (int i = 0; i < cnFields.length; i++) { Label label = new Label(i, 0, cnFields[i]); sheet.addCell(label); } // 填充内容 int rowNo = 1; for (int index = firstIndex; index <= lastIndex; index++) { // 获取单个对象 T item = list.get(index); for (int i = 0; i < enFields.length; i++) { Object objValue = getFieldValueByNameSequence(enFields[i], item); String fieldValue = objValue == null ? "" : objValue.toString(); Label label = new Label(i, rowNo, fieldValue); sheet.addCell(label); } rowNo++; } // 设置自动列宽 setColumnAutoSize(sheet, 5); } }