Excel在web項目裏的使用變得愈來愈普遍,特別是和線下耦合度較高的業務,Excel導入導出變得很是頻繁,儘管不少人寫了諸多的工具方法,可是終究沒有解決一個問題:有效的控制字段英文名稱和實際表頭名稱(這裏指Excel中文表頭名稱)的對應關係,在編碼開發過程當中,大量時間用於解決這些問題,並所以衍生出大量的工做量,以致於硬性的加了許多約定,在開發過程當中,工具方法通用性並不高。所以,基於實體Bean的Annotation化是一個比較不錯的嘗試。java
Excel導入到Beanweb
public class ExcelToBean{ private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private int etimes = 0; /** * 從文件讀取數據,最好是全部的單元格都是文本格式,日期格式要求yyyy-MM-dd HH:mm:ss,布爾類型0:真,1:假 * * @param edf * 數據格式化 * * @param file * Excel文件,支持xlsx後綴,xls的沒寫,基本同樣 * @return * @throws Exception */ @SuppressWarnings("unchecked") public <E> List<E> readFromFile(ExcelDataFormatter edf, File file, Class<?> clazz) throws Exception { Field[] fields = ReflectUtils.getClassFieldsAndSuperClassFields(clazz); Map<String, String> textToKey = new HashMap<String, String>(); Excel _excel = null; for (Field field : fields) { _excel = field.getAnnotation(Excel.class); if (_excel == null || _excel.skip() == true) { continue; } textToKey.put(_excel.name(), field.getName()); } InputStream is = new FileInputStream(file); Workbook wb = new XSSFWorkbook(is); Sheet sheet = wb.getSheetAt(0); Row title = sheet.getRow(0); // 標題數組,後面用到,根據索引去標題名稱,經過標題名稱去字段名稱用到 textToKey String[] titles = new String[title.getPhysicalNumberOfCells()]; for (int i = 0; i < title.getPhysicalNumberOfCells(); i++) { titles[i] = title.getCell(i).getStringCellValue(); } List<E> list = new ArrayList<E>(); E e = null; int rowIndex = 0; int columnCount = titles.length; Cell cell = null; Row row = null; for (Iterator<Row> it = sheet.rowIterator(); it.hasNext();) { row = it.next(); if (rowIndex++ == 0) { continue; } if (row == null) { break; } e = (E)clazz.newInstance(); for (int i = 0; i < columnCount; i++) { cell = row.getCell(i); if(null==cell)continue; etimes = 0; readCellContent(textToKey.get(titles[i]), fields, cell, e, edf); } list.add(e); } return list; } public static void main(String[] args) throws Exception { } /** * 從單元格讀取數據,根據不一樣的數據類型,使用不一樣的方式讀取<br> * 有時候常常和咱們期待的數據格式不同,會報異常,<br> * 咱們這裏採起強硬的方式<br> * 使用各類方法,知道嘗試到讀到數據爲止,而後根據Bean的數據類型,進行相應的轉換<br> * 若是嘗試完了(總共7次),仍是不能獲得數據,那麼拋個異常出來,沒辦法了 * * @param key * 當前單元格對應的Bean字段 * @param fields * Bean全部的字段數組 * @param cell * 單元格對象 * @param obj * @throws Exception */ public void readCellContent(String key, Field[] fields, Cell cell, Object obj, ExcelDataFormatter edf) throws Exception { Object o = null; try { switch (cell.getCellType()) { case XSSFCell.CELL_TYPE_BOOLEAN: o = cell.getBooleanCellValue(); break; case XSSFCell.CELL_TYPE_NUMERIC: o = cell.getNumericCellValue(); if (HSSFDateUtil.isCellDateFormatted(cell)) { o = DateUtil.getJavaDate(cell.getNumericCellValue()); } break; case XSSFCell.CELL_TYPE_STRING: o = cell.getStringCellValue(); break; case XSSFCell.CELL_TYPE_ERROR: o = cell.getErrorCellValue(); break; case XSSFCell.CELL_TYPE_BLANK: o = null; break; case XSSFCell.CELL_TYPE_FORMULA: o = cell.getCellFormula(); break; default: o = null; break; } if (o == null) return; for (Field field : fields) { field.setAccessible(true); if (field.getName().equals(key)) { Boolean bool = true; Map<String, String> map = null; if (edf == null) { bool = false; } else { map = edf.get(field.getName()); if (map == null) { bool = false; } } if (field.getType().equals(Date.class)) { if (o.getClass().equals(Date.class)) { field.set(obj, o); } else { field.set(obj, sdf.parse(o.toString())); } } else if (field.getType().equals(String.class)) { if (o.getClass().equals(String.class)) { field.set(obj, o); } else { field.set(obj, o.toString()); } } else if (field.getType().equals(Long.class)) { if (o.getClass().equals(Long.class)) { field.set(obj, o); } else { field.set(obj, Long.parseLong(o.toString())); } } else if (field.getType().equals(Integer.class)) { if (o.getClass().equals(Integer.class)) { field.set(obj, o); } else { // 檢查是否須要轉換 String ostr = o.toString(); ostr = ostr.split("\\.").length>0?ostr.split("\\.")[0]:ostr; if (bool) { field.set(obj, map.get(ostr) != null ? Integer.parseInt(map.get(ostr)) : Integer.parseInt(ostr)); } else { field.set(obj, Integer.parseInt(ostr)); } } } else if (field.getType().equals(BigDecimal.class)) { if (o.getClass().equals(BigDecimal.class)) { field.set(obj, o); } else { field.set(obj, BigDecimal.valueOf(Double.parseDouble(o.toString()))); } } else if (field.getType().equals(Boolean.class)) { if (o.getClass().equals(Boolean.class)) { field.set(obj, o); } else { // 檢查是否須要轉換 if (bool) { field.set(obj, map.get(o.toString()) != null ? Boolean.parseBoolean(map.get(o.toString())) : Boolean.parseBoolean(o.toString())); } else { field.set(obj, Boolean.parseBoolean(o.toString())); } } } else if (field.getType().equals(Float.class)) { if (o.getClass().equals(Float.class)) { field.set(obj, o); } else { field.set(obj, Float.parseFloat(o.toString())); } } else if (field.getType().equals(Double.class)) { if (o.getClass().equals(Double.class)) { field.set(obj, o); } else { field.set(obj, Double.parseDouble(o.toString())); } } } } } catch (Exception ex) { ex.printStackTrace(); // 若是仍是讀到的數據格式仍是不對,只能放棄了 if (etimes > 7) { throw ex; } etimes++; if (o == null) { readCellContent(key, fields, cell, obj, edf); } } } }
Bean導出到Excel數據庫
public class BeanToExcel { /** * 得到Workbook對象 * * @param list * 數據集合 * @return Workbook * @throws Exception */ public static <T> Workbook getWorkBook(List<T> list, ExcelDataFormatter edf) throws Exception { // 建立工做簿 Workbook wb = new SXSSFWorkbook(); if (list == null || list.size() == 0) return wb; // 建立一個工做表sheet Sheet sheet = wb.createSheet(); // 申明行 Row row = sheet.createRow(0); // 申明單元格 Cell cell = null; CreationHelper createHelper = wb.getCreationHelper(); Field[] fields = ReflectUtils.getClassFieldsAndSuperClassFields(list.get(0).getClass()); XSSFCellStyle titleStyle = (XSSFCellStyle) wb.createCellStyle(); titleStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); // 設置前景色 titleStyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(159, 213, 183))); titleStyle.setAlignment(CellStyle.ALIGN_CENTER); Font font = wb.createFont(); font.setColor(HSSFColor.BROWN.index); font.setBoldweight(Font.BOLDWEIGHT_BOLD); // 設置字體 titleStyle.setFont(font); int columnIndex = 0; Excel excel = null; for (Field field : fields) { field.setAccessible(true); excel = field.getAnnotation(Excel.class); if (excel == null || excel.skip() == true) { continue; } // 列寬注意乘256 sheet.setColumnWidth(columnIndex, excel.width() * 256); // 寫入標題 cell = row.createCell(columnIndex); cell.setCellStyle(titleStyle); cell.setCellValue(excel.name()); columnIndex++; } int rowIndex = 1; CellStyle cs = wb.createCellStyle(); for (T t : list) { row = sheet.createRow(rowIndex); columnIndex = 0; Object o = null; for (Field field : fields) { field.setAccessible(true); // 忽略標記skip的字段 excel = field.getAnnotation(Excel.class); if (excel == null || excel.skip() == true) { continue; } // 數據 cell = row.createCell(columnIndex); o = field.get(t); // 若是數據爲空,跳過 if (o == null) continue; // 處理日期類型 if (o instanceof Date) { // excel.dateFormat()獲取註解的日期格式,默認yyyy-MM-dd HH:mm:ss cs.setDataFormat(createHelper.createDataFormat().getFormat(excel.dateFormat())); cell.setCellStyle(cs); cell.setCellValue((Date) field.get(t)); } else if (o instanceof Double || o instanceof Float) {// 浮點數 cell.setCellValue(field.get(t).toString()); if (excel.precision() != -1) { cell.setCellValue(new BigDecimal(field.get(t).toString()).setScale(excel.precision(), excel.round() == true ? BigDecimal.ROUND_HALF_UP : BigDecimal.ROUND_FLOOR).toString()); } } else if (o instanceof BigDecimal) {// BigDecimal cell.setCellValue((field.get(t).toString())); if (excel.precision() != -1) { cell.setCellValue(new BigDecimal(field.get(t).toString()).setScale(excel.precision(), excel.round() == true ? BigDecimal.ROUND_HALF_UP : BigDecimal.ROUND_FLOOR).toString()); } } else if (o instanceof Boolean) {// 布爾類型 Boolean bool = (Boolean) field.get(t); if (edf == null) { cell.setCellValue(bool); } else { Map<String, String> map = edf.get(field.getName()); if (map == null) { cell.setCellValue(bool); } else { cell.setCellValue(map.get(bool.toString().toLowerCase())); } } } else if (o instanceof Integer) {// 整型 Integer intValue = (Integer) field.get(t); if (edf == null) { cell.setCellValue(intValue); } else { Map<String, String> map = edf.get(field.getName()); if (map == null) { cell.setCellValue(intValue); } else { cell.setCellValue(map.get(intValue.toString())); } } } else { cell.setCellValue(field.get(t).toString()); } columnIndex++; } rowIndex++; } return wb; } /** * 將數據寫入到EXCEL文檔 * * @param list * 數據集合 * @param edf * 數據格式化,好比有些數字表明的狀態,像是0:女,1:男,或者0:正常,1:鎖定,變成可讀的文字 * 該字段僅僅針對Boolean,Integer兩種類型做處理 * @param filePath * 文件路徑 * @throws Exception */ public static <T> void writeToFile(List<T> list, ExcelDataFormatter edf, String filePath) throws Exception { // 建立並獲取工做簿對象 Workbook wb = getWorkBook(list, edf); // 寫入到文件 FileOutputStream out = new FileOutputStream(filePath); wb.write(out); out.close(); }
數據格式化工具類數組
public class ExcelDataFormatter { private Map<String,Map<String,String>> formatter=new HashMap<String, Map<String,String>>(); public void set(String key,Map<String,String> map){ formatter.put(key, map); } public Map<String,String> get(String key){ return formatter.get(key); } }
反射工具類工具
public class ReflectUtils { /** * 獲取成員變量的修飾符 * * @param clazz * @param field * @return * @throws Exception */ public static <T> int getFieldModifier(Class<T> clazz, String field) throws Exception { // getDeclaredFields能夠獲取全部修飾符的成員變量,包括private,protected等getFields則不能夠 Field[] fields = clazz.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (fields[i].getName().equals(field)) { return fields[i].getModifiers(); } } throw new Exception(clazz + " has no field \"" + field + "\""); } /** * 獲取成員方法的修飾符 * * @param clazz * @param method * @return * @throws Exception */ public static <T> int getMethodModifier(Class<T> clazz, String method) throws Exception { // getDeclaredMethods能夠獲取全部修飾符的成員方法,包括private,protected等getMethods則不能夠 Method[] m = clazz.getDeclaredMethods(); for (int i = 0; i < m.length; i++) { if (m[i].getName().equals(m)) { return m[i].getModifiers(); } } throw new Exception(clazz + " has no method \"" + m + "\""); } /** * [對象]根據成員變量名稱獲取其值 * * @param clazzInstance * @param field * @return * @throws NoSuchFieldException * @throws SecurityException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static <T> Object getFieldValue(Object clazzInstance, Object field) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Field[] fields = clazzInstance.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (fields[i].getName().equals(field)) { // 對於私有變量的訪問權限,在這裏設置,這樣便可訪問Private修飾的變量 fields[i].setAccessible(true); return fields[i].get(clazzInstance); } } return null; } /** * [類]根據成員變量名稱獲取其值(默認值) * * @param clazz * @param field * @return * @throws NoSuchFieldException * @throws SecurityException * @throws IllegalArgumentException * @throws IllegalAccessException * @throws InstantiationException */ public static <T> Object getFieldValue(Class<T> clazz, String field) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException { Field[] fields = clazz.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (fields[i].getName().equals(field)) { // 對於私有變量的訪問權限,在這裏設置,這樣便可訪問Private修飾的變量 fields[i].setAccessible(true); return fields[i].get(clazz.newInstance()); } } return null; } /** * 獲取全部的成員變量(經過GET,SET方法獲取) * * @param clazz * @return */ public static <T> String[] getFields(Class<T> clazz) { Field[] fields = clazz.getDeclaredFields(); String[] fieldsArray = new String[fields.length]; for (int i = 0; i < fields.length; i++) { fieldsArray[i] = fields[i].getName(); } return fieldsArray; } /** * 獲取全部的成員變量,包括父類 * * @param clazz * @param superClass * 是否包括父類 * @return * @throws Exception */ public static <T> Field[] getFields(Class<T> clazz, boolean superClass) throws Exception { Field[] fields = clazz.getDeclaredFields(); Field[] superFields = null; if (superClass) { Class superClazz = clazz.getSuperclass(); if (superClazz != null) { superFields = superClazz.getDeclaredFields(); } } Field[] allFields = null; if (superFields == null || superFields.length == 0) { allFields = fields; } else { allFields = new Field[fields.length + superFields.length]; for (int i = 0; i < fields.length; i++) { allFields[i] = fields[i]; } for (int i = 0; i < superFields.length; i++) { allFields[fields.length + i] = superFields[i]; } } return allFields; } /** * 獲取全部的成員變量,包括父類 * * @param clazz * @return * @throws Exception */ public static <T> Field[] getClassFieldsAndSuperClassFields(Class<T> clazz) throws Exception { Field[] fields = clazz.getDeclaredFields(); if (clazz.getSuperclass() == null) { throw new Exception(clazz.getName() + "沒有父類"); } Field[] superFields = clazz.getSuperclass().getDeclaredFields(); Field[] allFields = new Field[fields.length + superFields.length]; for (int i = 0; i < fields.length; i++) { allFields[i] = fields[i]; } for (int i = 0; i < superFields.length; i++) { allFields[fields.length + i] = superFields[i]; } return allFields; } /** * 指定類,調用指定的無參方法 * * @param clazz * @param method * @throws NoSuchMethodException * @throws SecurityException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException * @throws InstantiationException */ public static <T> Object invoke(Class<T> clazz, String method) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { Object instance = clazz.newInstance(); Method m = clazz.getMethod(method, new Class[] {}); return m.invoke(instance, new Object[] {}); } /** * 經過對象,訪問其方法 * * @param clazzInstance * @param method * @return * @throws NoSuchMethodException * @throws SecurityException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException * @throws InstantiationException */ public static <T> Object invoke(Object clazzInstance, String method) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { Method m = clazzInstance.getClass().getMethod(method, new Class[] {}); return m.invoke(clazzInstance, new Object[] {}); } /** * 指定類,調用指定的方法 * * @param clazz * @param method * @param paramClasses * @param params * @return Object * @throws InstantiationException * @throws IllegalAccessException * @throws NoSuchMethodException * @throws SecurityException * @throws IllegalArgumentException * @throws InvocationTargetException */ public static <T> Object invoke(Class<T> clazz, String method, Class<T>[] paramClasses, Object[] params) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { Object instance = clazz.newInstance(); Method _m = clazz.getMethod(method, paramClasses); return _m.invoke(instance, params); } /** * 經過類的實例,調用指定的方法 * * @param clazzInstance * @param method * @param paramClasses * @param params * @return * @throws InstantiationException * @throws IllegalAccessException * @throws NoSuchMethodException * @throws SecurityException * @throws IllegalArgumentException * @throws InvocationTargetException */ public static <T> Object invoke(Object clazzInstance, String method, Class<T>[] paramClasses, Object[] params) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { Method _m = clazzInstance.getClass().getMethod(method, paramClasses); return _m.invoke(clazzInstance, params); } // @SuppressWarnings("unchecked") // public static void main(String[] args) throws Exception { // // getFields(User.class); // User u = new User(); // invoke(u, "setName", new Class[] { String.class }, new Object[] { "xx發大水法大水法x" }); // System.out.println(getFieldValue(u, "name")); // }
Annotation定義類字體
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.TYPE}) public @interface Excel { //列名 String name() default ""; //寬度 int width() default 20; //忽略該字段 boolean skip() default false; //日期格式化 String dateFormat() default "yyyy-MM-dd HH:mm:ss"; //浮點數的精度 int precision() default -1; //四捨五入 boolean round() default true; }
使用方法編碼
以Excel導入作示例:spa
以下圖所示的Excel數據excel
在寫代碼前,按照Annotation的定義對實體Bean進行添加相關標籤,例以下圖:code
示例代碼:
public class ElearnSysExcelService extends CustomService{ /** * 導入Excel文件數據到數據庫 * @param file * @author zhanglongping * @date 2016-11-10 下午2:10:24 */ public static void importExcelToDB(File file){ ExcelToBean e = new ExcelToBean(); ExcelDataFormatter edf = new ExcelDataFormatter(); Map<String,String> map = new HashMap<String,String>(); map.put( "準備就緒","111"); map.put( "開始","222"); map.put( "工做","333"); map.put( "結束","444"); edf.set("state", map); try { List<ElearnSysUser> list = e.readFromFile(edf, file, ElearnSysUser.class); for(ElearnSysUser esu:list){ System.out.println(esu.getUserName()+"~~~"+esu.getState()+"~~~"+esu.getRealName()); } } catch (Exception e1) { e1.printStackTrace(); } } public static void main(String[] args) { ElearnSysExcelService e = new ElearnSysExcelService(); File file = new File("f://test/excel_sys_user.xlsx"); e.importExcelToDB(file); } }
運行結果:
能夠看到,Excel中的狀態數據已被轉換爲111,444等數據
參考文獻:http://www.xdemo.org