導出excel作為很多頁面的常用功能,但是不同頁面導出的數據內容和字段不一,不方便操作,本文通過注解和工具類來解決excel通用導出問題
1.注解
通過注解來定義excel字段的名稱,排序和日期格式
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface ExcelField { /** * excel列名 */ String value() default ""; /** * 排序 */ int sort() default 0; /** * 日期格式 */ String dateFormat() default ""; }
2.實體
定義要導出的數據實體,要導出的字段上加上注解
@Data public class DataVO { /** * 設備編號 */ @ExcelField(value = "設備編號", sort = 1) private String deviceNo; /** * 創建時間 */ @ExcelField(value = "日期", sort = 2, dateFormat = "yyyy-MM-dd") private Date createTime; }
3.導出入口函數
public static <T> void getExcelDate(String title, List<T> list, HttpServletRequest request, HttpServletResponse response) { //獲取導出數據 List<Map<String, Object>> list2 = DataUtil.getModelExcelFields(list); // 列名 Map<String, String[]> map = DataUtil.getModelExcelFieldsToArray(list.get(0).getClass()); String[] column = map.get("array"); String[] columnName = map.get("arrayName"); // 把數據綁定到對應的字段下 List<Object[]> dataList = new ArrayList<Object[]>(); Object[] objs; for (int i = 0; i < list2.size(); i++) { objs = new Object[columnName.length]; for (int j = 0; j < columnName.length; j++) { objs[j] = list2.get(i).get(column[j]); } dataList.add(objs); } exportExcel(title, columnName, dataList, request, response); }
4.獲取導出數據
實體類中標記了注解的字段才會導出,根據反射獲取字段類型和值,其中Date類型的數據轉換成String類型時,通過注解的日期格式來格式化日期

public static <T> List<Map<String, Object>> getModelExcelFields(List<T> models) { List<Map<String, Object>> list = new ArrayList<>(); if (!models.isEmpty()) { for (T model : models) { Map<String, Object> map = new HashMap<>(16); // 獲取實體類的所有屬性,返回Field數組 Field[] fields = model.getClass().getDeclaredFields(); try { // 遍歷所有屬性 for (Field field : fields) { ExcelField anno = field.getAnnotation(ExcelField.class); if (anno == null) { continue; } // 獲取屬性的名字 String fieldName = field.getName(); // 將屬性的首字符大寫,方便構造get,set方法 String name = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); // 獲取屬性的類型 String type = field.getGenericType().toString(); // 如果type是類類型,則前面包含"class ",后面跟類名 if ("class java.lang.String".equals(type)) { Method m = model.getClass().getMethod("get" + name); // 調用getter方法獲取屬性值 String value = (String) m.invoke(model); if (value != null && (!"".equals(value))) { map.put(name, value); } continue; } if ("int".equals(type)) { Method m = model.getClass().getMethod("get" + name); int value = (int) m.invoke(model); map.put(name, value); continue; } if ("double".equals(type)) { Method m = model.getClass().getMethod("get" + name); double value = (double) m.invoke(model); map.put(name, value); continue; } if ("class java.lang.Double".equals(type)) { Method m = model.getClass().getMethod("get" + name); Double value = (Double) m.invoke(model); if (value != null) { map.put(name, value); } continue; } if ("class java.math.BigDecimal".equals(type)) { Method m = model.getClass().getMethod("get" + name); BigDecimal value = (BigDecimal) m.invoke(model); if (value != null) { map.put(name, value); } continue; } if ("class java.lang.Boolean".equals(type)) { Method m = model.getClass().getMethod("get" + name); Boolean value = (Boolean) m.invoke(model); if (value != null) { map.put(name, value); } continue; } if ("long".equals(type) && !("SerialVersionUID".equals(name))) { Method m = model.getClass().getMethod("get" + name); long value = (long) m.invoke(model); if (value != 0) { map.put(name, value); } continue; } if ("class java.lang.Long".equals(type)) { Method m = model.getClass().getMethod("get" + name); Long value = (Long) m.invoke(model); if (value != null) { map.put(name, value); } continue; } if ("class java.util.Date".equals(type)) { Method m = model.getClass().getMethod("get" + name); Date value = (Date) m.invoke(model); String date = null; if (value != null) { try { if (StringUtil.isNotEmpty(anno.dateFormat())) { date = DateTools.formatDate(value, anno.dateFormat()); } else { date = DateTools.formatDate(value, DateTools.DATE_PATTERN_DEFAULT); } } catch (ParseException e) { e.printStackTrace(); } map.put(name, date); } continue; } if ("class java.lang.Integer".equals(type)) { Method m = model.getClass().getMethod("get" + name); Integer value = (Integer) m.invoke(model); if (value != null) { map.put(name, value); } continue; } if ("class java.lang.Short".equals(type)) { Method m = model.getClass().getMethod("get" + name); Short value = (Short) m.invoke(model); if (value != null) { map.put(name, value); } } } } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } list.add(map); } } return list; }
5.獲取列名
實體類中標記了注解的字段才會導出,根據注解的sort進行列的排序
public static Map<String, String[]> getModelExcelFieldsToArray(Class clazz) { Map<String, String[]> map = new HashMap<>(16); // 獲取實體類的所有屬性,返回Field數組 Field[] fields = clazz.getDeclaredFields(); List<String> list = new ArrayList<>(); List<String> listName = new ArrayList<>(); // 遍歷所有屬性 Arrays.stream(fields).filter(field -> { ExcelField anno = field.getAnnotation(ExcelField.class); return anno != null; }).sorted(Comparator.comparing(field -> { ExcelField anno = field.getAnnotation(ExcelField.class); return anno.sort(); })).forEach(field -> { ExcelField anno = field.getAnnotation(ExcelField.class); // 獲取屬性的名字 String fieldName = field.getName(); // 將屬性的首字符大寫,方便構造get,set方法 String name = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); list.add(name); listName.add(anno.value()); }); String[] array = new String[list.size()]; String[] arrayName = new String[list.size()]; for (int i = 0; i < list.size(); i++) { array[i] = list.get(i); arrayName[i] = listName.get(i); } map.put("array", array); map.put("arrayName", arrayName); return map; }
6.生成excel文件導出

public static void exportExcel(String title, String[] columnName, List<Object[]> dataList, HttpServletRequest request, HttpServletResponse response) { response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); // 實例化工具類 ExportExcel ex = new ExportExcel(title, columnName, dataList, request, response); try { // 導出excel ex.export(null); } catch (Exception e) { e.printStackTrace(); } } public void export(String path) throws Exception { try { HSSFWorkbook workbook = getWorkbook(); String fileName = title + "_" + String.valueOf(System.currentTimeMillis()).substring(4, 13) + ".xls"; String headStr = "attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\""; if (StringUtil.isNull(path)) { try { response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", headStr); OutputStream out1 = response.getOutputStream(); workbook.write(out1); } catch (IOException e) { e.printStackTrace(); } } else { //判斷是否存在目錄. 不存在則創建 if (!FileUtil.exists(path)) { FileUtil.createFolder(path); } //輸出Excel文件 FileOutputStream out1 = new FileOutputStream(path + File.separator+ fileName); workbook.write(out1); out1.close(); } } catch (Exception e) { e.printStackTrace(); } } private HSSFWorkbook getWorkbook() { // 創建工作簿對象 HSSFWorkbook workbook = new HSSFWorkbook(); // 創建工作表 HSSFSheet sheet = workbook.createSheet(title); // 產生表格標題行 HSSFRow rowm = sheet.createRow(0); HSSFCell cellTiltle = rowm.createCell(0); //設置標題和單元格樣式 //獲取列頭樣式對象 HSSFCellStyle columnTopStyle = this.getColumnTopStyle(workbook); //單元格樣式對象 HSSFCellStyle style = this.getStyle(workbook); //合並單元格 sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, (columnName.length))); cellTiltle.setCellStyle(columnTopStyle); cellTiltle.setCellValue(title); // 定義所需列數 int columnNum = columnName.length + 1; // 在索引2的位置創建行(最頂端的行開始的第二行) HSSFRow rowRowName = sheet.createRow(2); // 將列頭設置到sheet的單元格中 for (int n = 0; n < columnNum; n++) { if (n == 0) { //創建列頭對應個數的單元格 HSSFCell cellRowName = rowRowName.createCell(n); //設置列頭單元格的數據類型 cellRowName.setCellType(CellType.STRING); //設置列頭單元格的值 cellRowName.setCellValue("序號"); //設置列頭單元格樣式 cellRowName.setCellStyle(columnTopStyle); } else { //創建列頭對應個數的單元格 HSSFCell cellRowName = rowRowName.createCell(n); //設置列頭單元格的數據類型 cellRowName.setCellType(CellType.STRING); HSSFRichTextString text = new HSSFRichTextString(columnName[n - 1]); //設置列頭單元格的值 cellRowName.setCellValue(text); //設置列頭單元格樣式 cellRowName.setCellStyle(columnTopStyle); } } //將查詢出的數據設置到sheet對應的單元格中 for (int i = 0; i < dataList.size(); i++) { //遍歷每個對象 Object[] obj = dataList.get(i); //創建所需的行數 HSSFRow row = sheet.createRow(i + 3); for (int j = 0; j < obj.length + 1; j++) { //設置單元格的數據類型 HSSFCell cell; if (j == 0) { cell = row.createCell(j, CellType.NUMERIC); cell.setCellValue(i + 1); } else { //其余列都為字符串類型並設置單元格的值 cell = row.createCell(j, CellType.STRING); if (!"".equals(obj[j - 1]) && obj[j - 1] != null) { cell.setCellValue(obj[j - 1].toString()); } else { cell.setCellValue(" "); } } //設置單元格樣式 cell.setCellStyle(style); } } //讓列寬隨着導出的列長自動適應 for (int colNum = 0; colNum < columnNum; colNum++) { int columnWidth = sheet.getColumnWidth(colNum) / 256; for (int rowNum = 0; rowNum < sheet.getLastRowNum(); rowNum++) { HSSFRow currentRow; //當前行未被使用過 if (sheet.getRow(rowNum) == null) { currentRow = sheet.createRow(rowNum); } else { currentRow = sheet.getRow(rowNum); } if (currentRow.getCell(colNum) != null) { //取得當前的單元格 HSSFCell currentCell = currentRow.getCell(colNum); //如果當前單元格類型為字符串 if (currentCell.getCellType() == CellType.STRING) { int length = currentCell.getStringCellValue().getBytes().length; if (columnWidth < length) { //將單元格里面值大小作為列寬度 columnWidth = length; } } } } //再根據不同列單獨做下處理 if (colNum == 0) { sheet.setColumnWidth(colNum, (columnWidth - 2) * 200); } else { if (columnWidth < 252) { sheet.setColumnWidth(colNum, (columnWidth + 4) * 256); } else { sheet.setColumnWidth(colNum, 6000); } } } return workbook; }
至此,導出完成。