使用JXL匯入匯出Excel的Java工具類ExcelUtil(實際專案使用)
阿新 • • 發佈:2018-12-29
引用地址:https://www.cnblogs.com/qilihu/p/6198622.html
maven引用:
<dependency>
<groupId>net.sourceforge.jexcelapi</groupId>
<artifactId>jxl</artifactId>
<version>2.6.12</version>
</dependency>
package com.hongniu.service.util; import jxl.Cell; import jxl.Sheet; import jxl.Workbook; import jxl.format.Alignment; import jxl.format.UnderlineStyle; import jxl.write.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.*; /** * @author qlinc * @projectName hongniu_serverManage * @description * @date 18/11/28 14:05 */ public class ExcelUtilTo { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * @param list 資料來源 * @param fieldMap 類的英文屬性和Excel中的中文列名的對應關係 * 如果需要的是引用物件的屬性,則英文屬性使用類似於EL表示式的格式 * 如:list中存放的都是student,student中又有college屬性,而我們需要名稱,則可以這樣寫 * fieldMap.put("college.collegeName","名稱") * @param sheetName 工作表的名稱 * @param sheetSize 每個工作表中記錄的最大個數 * @param out 匯出流 * 匯入和匯出方法都是通過傳一個fieldMap引數(類的英文屬性和Excel的中文列頭的對應關係)來連線實體類和Excel的 * * 匯出的時候可以選擇匯出到本地檔案系統或匯出到瀏覽器,也可以自定義每個工作表的大小 * 匯入的時候可以自定義業務主鍵組合uniqueFields,這樣就可以檢測Excel中是否有重複行了 * @MethodName : listToExcel * @Description : 匯出Excel(可以匯出到本地檔案系統,也可以匯出到瀏覽器,可自定義工作表大小) */ public static <T> void listToExcel(List<T> list, LinkedHashMap<String, String> fieldMap, String sheetName, int sheetSize, OutputStream out) { if (list.size() == 0 || list == null) { throw new RuntimeException("資料來源中沒有任何資料"); } if (sheetSize > 65535 || sheetSize < 1) { sheetSize = 65535; } //建立工作簿併發送到OutputStream指定的地方 WritableWorkbook wwb; try { wwb = Workbook.createWorkbook(out); //因為2003的Excel一個工作表最多可以有65536條記錄,除去列頭剩下65535條 //所以如果記錄太多,需要放到多個工作表中,其實就是個分頁的過程 //1.計算一共有多少個工作表 double sheetNum = Math.ceil(list.size() / new Integer(sheetSize).doubleValue()); //2.建立相應的工作表,並向其中填充資料 for (int i = 0; i < sheetNum; i++) { //如果只有一個工作表的情況 if (1 == sheetNum) { 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) { e.printStackTrace(); } } /** * @param list 資料來源 * @param fieldMap 類的英文屬性和Excel中的中文列名的對應關係 * @param out 匯出流 * @MethodName : listToExcel * @Description : 匯出Excel(可以匯出到本地檔案系統,也可以匯出到瀏覽器,工作表大小為2003支援的最大值) */ public static <T> void listToExcel( List<T> list, LinkedHashMap<String, String> fieldMap, String sheetName, OutputStream out ) { listToExcel(list, fieldMap, sheetName, 65535, out); } /** * @param list 資料來源 * @param fieldMap 類的英文屬性和Excel中的中文列名的對應關係 * @param sheetSize 每個工作表中記錄的最大個數 * @param response 使用response可以匯出到瀏覽器 * @MethodName : listToExcel * @Description : 匯出Excel(匯出到瀏覽器,可以自定義工作表的大小) */ public static <T> void listToExcel( List<T> list, LinkedHashMap<String, String> fieldMap, String sheetName, int sheetSize, HttpServletResponse response ) { //設定預設檔名為當前時間:年月日時分秒 String time = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date()).toString(); //設定response頭資訊 response.reset(); //改成輸出excel檔案 response.setContentType("application/vnd.ms-excel"); OutputStream out = null; try { response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(sheetName + time, "UTF-8") + ".xls"); //建立工作簿併發送到瀏覽器 out = response.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } listToExcel(list, fieldMap, sheetName, sheetSize, out); } /** * @param list 資料來源 * @param fieldMap 類的英文屬性和Excel中的中文列名的對應關係 * @param response 使用response可以匯出到瀏覽器 * @MethodName : listToExcel * @Description : 匯出Excel(匯出到瀏覽器,工作表的大小是2003支援的最大值) */ public static <T> void listToExcel( List<T> list, LinkedHashMap<String, String> fieldMap, String sheetName, HttpServletResponse response ) { listToExcel(list, fieldMap, sheetName, 65535, response); } /** * @param in :承載著Excel的輸入流 * @param entityClass :List中物件的型別(Excel中的每一行都要轉化為該型別的物件) * @param fieldMap :Excel中的中文列頭和類的英文屬性的對應關係Map * @param uniqueFields :指定業務主鍵組合(即複合主鍵),這些列的組合不能重複 * @return :List * @MethodName : excelToList * @Description : 將Excel轉化為List */ public static <T> List<T> excelToList( InputStream in, String sheetName, Class<T> entityClass, LinkedHashMap<String, String> fieldMap, String[] uniqueFields ) { //定義要返回的list List<T> resultList = new ArrayList<T>(); try { //根據Excel資料來源建立WorkBook Workbook wb = Workbook.getWorkbook(in); //獲取工作表 Sheet sheet = wb.getSheet(sheetName); //獲取工作表的有效行數 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++; } } //如果Excel中沒有資料則提示錯誤 if (realRows <= 1) { 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<String> excelFieldList = Arrays.asList(excelFieldNames); for (String cnName : fieldMap.keySet()) { if (!excelFieldList.contains(cnName)) { isExist = false; break; } } //如果有列名不存在,則丟擲異常,提示錯誤 if (!isExist) { throw new RuntimeException("Excel中缺少必要的欄位,或欄位名稱有誤"); } //將列名和列號放入Map中,這樣通過列名就可以拿到列號 LinkedHashMap<String, Integer> colMap = new LinkedHashMap<String, Integer>(); for (int i = 0; i < excelFieldNames.length; i++) { colMap.put(excelFieldNames[i], firstRow[i].getColumn()); } //判斷是否有重複行 //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, 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 (Map.Entry<String, String> entry : fieldMap.entrySet()) { //獲取中文欄位名 String cnNormalName = entry.getKey(); //獲取英文欄位名 String enNormalName = entry.getValue(); //根據中文欄位名獲取列號 int col = colMap.get(cnNormalName); //獲取當前單元格中的內容 String content = sheet.getCell(col, i).getContents().toString().trim(); //給物件賦值 setFieldValueByName(enNormalName, content, entity); } resultList.add(entity); } } catch (Exception e) { e.printStackTrace(); //如果是ExcelException,則直接丟擲 if (e instanceof RuntimeException) { throw (RuntimeException) e; //否則將其它異常包裝成ExcelException再丟擲 } else { e.printStackTrace(); throw new RuntimeException("匯入Excel失敗"); } } return resultList; } /*<-------------------------輔助的私有方法----------------------------------------------->*/ /** * @param fieldName 欄位名 * @param o 物件 * @return 欄位值 * @MethodName : getFieldValueByName * @Description : 根據欄位名獲取欄位值 */ 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); if (value != null || value != "") { // 取得對應getXxx()方法 String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); // 泛型為Object以及所有Object的子類 Class<? extends Object> tCls = o.getClass(); // 通過方法名得到對應的方法 Method getMethod = tCls.getMethod(getMethodName, new Class[]{}); // 動態呼叫方,得到屬性值 value = getMethod.invoke(o, new Object[]{}); } if (value instanceof Date) { value = sdf.format(value); } } else { throw new RuntimeException(o.getClass().getSimpleName() + "類不存在欄位名 " + fieldName); } return value; } /** * @param fieldName 欄位名 * @param clazz 包含該欄位的類 * @return 欄位 * @MethodName : getFieldByName * @Description : 根據欄位名獲取欄位 */ 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; } /** * @param fieldNameSequence 帶路徑的屬性名或簡單屬性名 * @param o 物件 * @return 屬性值 * @throws Exception * @MethodName : getFieldValueByNameSequence * @Description : * 根據帶路徑或不帶路徑的屬性名獲取屬性值 * 即接受簡單屬性名,如userName等,又接受帶路徑的屬性名,如student.department.name等 */ 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; } /** * @param fieldName 欄位名 * @param fieldValue 欄位值 * @param o 物件 * @MethodName : setFieldValueByName * @Description : 根據欄位名給物件的欄位賦值 */ 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) { if ((fieldValue != null) && (fieldValue.toString().length() > 0)) { field.set(o, Character .valueOf(fieldValue.toString().charAt(0))); } } else if (Date.class == fieldType) { field.set(o, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(fieldValue.toString())); } else { field.set(o, fieldValue); } } else { throw new RuntimeException(o.getClass().getSimpleName() + "類不存在欄位名 " + fieldName); } } /** * @param ws * @MethodName : setColumnAutoSize * @Description : 設定工作表自動列寬和首行加粗 */ 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); } } /** * @param sheet 工作表 * @param list 資料來源 * @param fieldMap 中英文欄位對應關係的Map * @param firstIndex 開始索引 * @param lastIndex 結束索引 * @MethodName : fillSheet * @Description : 向工作表中填充資料 */ private static <T> void fillSheet( WritableSheet sheet, List<T> list, LinkedHashMap<String, String> fieldMap, int firstIndex, int lastIndex ) throws Exception { //定義存放英文欄位名和中文欄位名的陣列 String[] enFields = new String[fieldMap.size()]; String[] cnFields = new String[fieldMap.size()]; //填充陣列 int count = 0; for (Map.Entry<String, String> entry : fieldMap.entrySet()) { enFields[count] = entry.getKey(); cnFields[count] = entry.getValue(); count++; } //1.定義表頭單元格樣式 //構造格式:ARIAL字型、10號、粗體、非斜體、無下劃線、黑色 WritableFont wfHead = new WritableFont(WritableFont.createFont("宋體"),14,WritableFont.BOLD, false,UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.BLACK); // 單元格定義 WritableCellFormat wcfHead = new WritableCellFormat(wfHead); // 設定對齊方式 wcfHead.setAlignment(Alignment.CENTRE); //1.定義表頭單元格樣式 //構造格式:ARIAL字型、10號、粗體、非斜體、無下劃線、黑色 WritableFont wfContent = new WritableFont(WritableFont.createFont("宋體"),12,WritableFont.NO_BOLD, false,UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.BLACK); // 單元格定義 WritableCellFormat wcfContent = new WritableCellFormat(wfContent); // 設定單元格的背景顏色 // wcf.setBackground(jxl.format.Colour.BLACK); // 設定對齊方式 wcfContent.setAlignment(Alignment.LEFT); //填充表頭 for (int i = 0; i < cnFields.length; i++) { Label label = new Label(i, 0, cnFields[i],wcfHead); 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,wcfContent); sheet.addCell(label); } rowNo++; } //設定自動列寬 setColumnAutoSize(sheet, 5); } }