excel工具类
來自:https://github.com/SargerasWang/ExcelUtil.git
package com.sargeraswang.util.ExcelUtil;import org.apache.commons.beanutils.BeanComparator; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.ComparatorUtils; import org.apache.commons.collections.comparators.ComparableComparator; import org.apache.commons.collections.comparators.ComparatorChain; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.util.CellReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.text.MessageFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*;/*** The <code>ExcelUtil</code> 與 {@link ExcelCell}搭配使用** @author sargeras.wang* @version 1.0, Created at 2013年9月14日*/ public class ExcelUtil {private static Logger LG = LoggerFactory.getLogger(ExcelUtil.class);/*** 用來驗證excel與Vo中的類型是否一致 <br>* Map<欄位類型,只能是哪些Cell類型>*/private static Map<Class<?>, Integer[]> validateMap = new HashMap<Class<?>, Integer[]>();static {validateMap.put(String[].class, new Integer[]{Cell.CELL_TYPE_STRING});validateMap.put(Double[].class, new Integer[]{Cell.CELL_TYPE_NUMERIC});validateMap.put(String.class, new Integer[]{Cell.CELL_TYPE_STRING});validateMap.put(Double.class, new Integer[]{Cell.CELL_TYPE_NUMERIC});validateMap.put(Date.class, new Integer[]{Cell.CELL_TYPE_NUMERIC, Cell.CELL_TYPE_STRING});validateMap.put(Integer.class, new Integer[]{Cell.CELL_TYPE_NUMERIC});validateMap.put(Float.class, new Integer[]{Cell.CELL_TYPE_NUMERIC});validateMap.put(Long.class, new Integer[]{Cell.CELL_TYPE_NUMERIC});validateMap.put(Boolean.class, new Integer[]{Cell.CELL_TYPE_BOOLEAN});}/*** 獲取cell類型的文字描述** @param cellType <pre>* Cell.CELL_TYPE_BLANK* Cell.CELL_TYPE_BOOLEAN* Cell.CELL_TYPE_ERROR* Cell.CELL_TYPE_FORMULA* Cell.CELL_TYPE_NUMERIC* Cell.CELL_TYPE_STRING* </pre>* @return*/private static String getCellTypeByInt(int cellType) {switch (cellType) {case Cell.CELL_TYPE_BLANK:return "Null type";case Cell.CELL_TYPE_BOOLEAN:return "Boolean type";case Cell.CELL_TYPE_ERROR:return "Error type";case Cell.CELL_TYPE_FORMULA:return "Formula type";case Cell.CELL_TYPE_NUMERIC:return "Numeric type";case Cell.CELL_TYPE_STRING:return "String type";default:return "Unknown type";}}/*** 獲取單元格值** @param cell* @return*/private static Object getCellValue(Cell cell) {if (cell == null|| (cell.getCellType() == Cell.CELL_TYPE_STRING && StringUtils.isBlank(cell.getStringCellValue()))) {return null;}int cellType = cell.getCellType();switch (cellType) {case Cell.CELL_TYPE_BLANK:return null;case Cell.CELL_TYPE_BOOLEAN:return cell.getBooleanCellValue();case Cell.CELL_TYPE_ERROR:return cell.getErrorCellValue();case Cell.CELL_TYPE_FORMULA:return cell.getNumericCellValue();case Cell.CELL_TYPE_NUMERIC:return cell.getNumericCellValue();case Cell.CELL_TYPE_STRING:return cell.getStringCellValue();default:return null;}}/*** 利用JAVA的反射機制,將放置在JAVA集合中并且符號一定條件的數(shù)據(jù)以EXCEL 的形式輸出到指定IO設(shè)備上<br>* 用于單個sheet** @param <T>* @param headers 表格屬性列名數(shù)組* @param dataset 需要顯示的數(shù)據(jù)集合,集合中一定要放置符合javabean風(fēng)格的類的對象。此方法支持的* javabean屬性的數(shù)據(jù)類型有基本數(shù)據(jù)類型及String,Date,String[],Double[]* @param out 與輸出設(shè)備關(guān)聯(lián)的流對象,可以將EXCEL文檔導(dǎo)出到本地文件或者網(wǎng)絡(luò)中*/public static <T> void exportExcel(String[] headers, Collection<T> dataset, OutputStream out) {exportExcel(headers, dataset, out, null);}/*** 利用JAVA的反射機制,將放置在JAVA集合中并且符號一定條件的數(shù)據(jù)以EXCEL 的形式輸出到指定IO設(shè)備上<br>* 用于單個sheet** @param <T>* @param headers 表格屬性列名數(shù)組* @param dataset 需要顯示的數(shù)據(jù)集合,集合中一定要放置符合javabean風(fēng)格的類的對象。此方法支持的* javabean屬性的數(shù)據(jù)類型有基本數(shù)據(jù)類型及String,Date,String[],Double[]* @param out 與輸出設(shè)備關(guān)聯(lián)的流對象,可以將EXCEL文檔導(dǎo)出到本地文件或者網(wǎng)絡(luò)中* @param pattern 如果有時間數(shù)據(jù),設(shè)定輸出格式。默認為"yyy-MM-dd"*/public static <T> void exportExcel(String[] headers, Collection<T> dataset, OutputStream out,String pattern) {// 聲明一個工作薄HSSFWorkbook workbook = new HSSFWorkbook();// 生成一個表格HSSFSheet sheet = workbook.createSheet();write2Sheet(sheet, headers, dataset, pattern);try {workbook.write(out);} catch (IOException e) {LG.error(e.toString(), e);}}public static void exportExcel(String[][] datalist, OutputStream out) {try {// 聲明一個工作薄HSSFWorkbook workbook = new HSSFWorkbook();// 生成一個表格HSSFSheet sheet = workbook.createSheet();for (int i = 0; i < datalist.length; i++) {String[] r = datalist[i];HSSFRow row = sheet.createRow(i);for (int j = 0; j < r.length; j++) {HSSFCell cell = row.createCell(j);//cell max length 32767if (r[j].length() > 32767) {r[j] = "--此字段過長(超過32767),已被截斷--" + r[j];r[j] = r[j].substring(0, 32766);}cell.setCellValue(r[j]);}}//自動列寬if (datalist.length > 0) {int colcount = datalist[0].length;for (int i = 0; i < colcount; i++) {sheet.autoSizeColumn(i);}}workbook.write(out);} catch (IOException e) {LG.error(e.toString(), e);}}/*** 利用JAVA的反射機制,將放置在JAVA集合中并且符號一定條件的數(shù)據(jù)以EXCEL 的形式輸出到指定IO設(shè)備上<br>* 用于多個sheet** @param <T>* @param sheets {@link ExcelSheet}的集合* @param out 與輸出設(shè)備關(guān)聯(lián)的流對象,可以將EXCEL文檔導(dǎo)出到本地文件或者網(wǎng)絡(luò)中*/public static <T> void exportExcel(List<ExcelSheet<T>> sheets, OutputStream out) {exportExcel(sheets, out, null);}/*** 利用JAVA的反射機制,將放置在JAVA集合中并且符號一定條件的數(shù)據(jù)以EXCEL 的形式輸出到指定IO設(shè)備上<br>* 用于多個sheet** @param <T>* @param sheets {@link ExcelSheet}的集合* @param out 與輸出設(shè)備關(guān)聯(lián)的流對象,可以將EXCEL文檔導(dǎo)出到本地文件或者網(wǎng)絡(luò)中* @param pattern 如果有時間數(shù)據(jù),設(shè)定輸出格式。默認為"yyy-MM-dd"*/public static <T> void exportExcel(List<ExcelSheet<T>> sheets, OutputStream out, String pattern) {if (CollectionUtils.isEmpty(sheets)) {return;}// 聲明一個工作薄HSSFWorkbook workbook = new HSSFWorkbook();for (ExcelSheet<T> sheet : sheets) {// 生成一個表格HSSFSheet hssfSheet = workbook.createSheet(sheet.getSheetName());write2Sheet(hssfSheet, sheet.getHeaders(), sheet.getDataset(), pattern);}try {workbook.write(out);} catch (IOException e) {LG.error(e.toString(), e);}}/*** 每個sheet的寫入** @param sheet 頁簽* @param headers 表頭* @param dataset 數(shù)據(jù)集合* @param pattern 日期格式*/private static <T> void write2Sheet(HSSFSheet sheet, String[] headers, Collection<T> dataset,String pattern) {// 產(chǎn)生表格標(biāo)題行HSSFRow row = sheet.createRow(0);for (int i = 0; i < headers.length; i++) {HSSFCell cell = row.createCell(i);HSSFRichTextString text = new HSSFRichTextString(headers[i]);cell.setCellValue(text);}// 遍歷集合數(shù)據(jù),產(chǎn)生數(shù)據(jù)行Iterator<T> it = dataset.iterator();int index = 0;while (it.hasNext()) {index++;row = sheet.createRow(index);T t = (T) it.next();try {if (t instanceof Map) {@SuppressWarnings("unchecked")Map<String, Object> map = (Map<String, Object>) t;int cellNum = 0;for (String k : headers) {if (map.containsKey(k) == false) {LG.error("Map 中 不存在 key [" + k + "]");continue;}Object value = map.get(k);HSSFCell cell = row.createCell(cellNum);cell.setCellValue(String.valueOf(value));cellNum++;}} else {List<FieldForSortting> fields = sortFieldByAnno(t.getClass());int cellNum = 0;for (int i = 0; i < fields.size(); i++) {HSSFCell cell = row.createCell(cellNum);Field field = fields.get(i).getField();field.setAccessible(true);Object value = field.get(t);String textValue = null;if (value instanceof Integer) {int intValue = (Integer) value;cell.setCellValue(intValue);} else if (value instanceof Float) {float fValue = (Float) value;cell.setCellValue(fValue);} else if (value instanceof Double) {double dValue = (Double) value;cell.setCellValue(dValue);} else if (value instanceof Long) {long longValue = (Long) value;cell.setCellValue(longValue);} else if (value instanceof Boolean) {boolean bValue = (Boolean) value;cell.setCellValue(bValue);} else if (value instanceof Date) {Date date = (Date) value;SimpleDateFormat sdf = new SimpleDateFormat(pattern);textValue = sdf.format(date);} else if (value instanceof String[]) {String[] strArr = (String[]) value;for (int j = 0; j < strArr.length; j++) {String str = strArr[j];cell.setCellValue(str);if (j != strArr.length - 1) {cellNum++;cell = row.createCell(cellNum);}}} else if (value instanceof Double[]) {Double[] douArr = (Double[]) value;for (int j = 0; j < douArr.length; j++) {Double val = douArr[j];// 資料不為空則set Valueif (val != null) {cell.setCellValue(val);}if (j != douArr.length - 1) {cellNum++;cell = row.createCell(cellNum);}}} else {// 其它數(shù)據(jù)類型都當(dāng)作字符串簡單處理String empty = StringUtils.EMPTY;ExcelCell anno = field.getAnnotation(ExcelCell.class);if (anno != null) {empty = anno.defaultValue();}textValue = value == null ? empty : value.toString();}if (textValue != null) {HSSFRichTextString richString = new HSSFRichTextString(textValue);cell.setCellValue(richString);}cellNum++;}}} catch (Exception e) {LG.error(e.toString(), e);}}// 設(shè)定自動寬度for (int i = 0; i < headers.length; i++) {sheet.autoSizeColumn(i);}}/*** 把Excel的數(shù)據(jù)封裝成voList** @param clazz vo的Class* @param inputStream excel輸入流* @param pattern 如果有時間數(shù)據(jù),設(shè)定輸入格式。默認為"yyy-MM-dd"* @param logs 錯誤log集合* @param arrayCount 如果vo中有數(shù)組類型,那就按照index順序,把數(shù)組應(yīng)該有幾個值寫上.* @return voList* @throws RuntimeException*/@SuppressWarnings("unchecked")public static <T> Collection<T> importExcel(Class<T> clazz, InputStream inputStream,String pattern, ExcelLogs logs, Integer... arrayCount) {HSSFWorkbook workBook = null;try {workBook = new HSSFWorkbook(inputStream);} catch (IOException e) {LG.error(e.toString(), e);}List<T> list = new ArrayList<T>();HSSFSheet sheet = workBook.getSheetAt(0);Iterator<Row> rowIterator = sheet.rowIterator();try {List<ExcelLog> logList = new ArrayList<ExcelLog>();// Map<title,index>Map<String, Integer> titleMap = new HashMap<>();while (rowIterator.hasNext()) {Row row = rowIterator.next();if (row.getRowNum() == 0) {if (clazz == Map.class) {// 解析map用的key,就是excel標(biāo)題行Iterator<Cell> cellIterator = row.cellIterator();Integer index = 0;while (cellIterator.hasNext()) {String value = cellIterator.next().getStringCellValue();titleMap.put(value, index);index++;}}continue;}// 整行都空,就跳過boolean allRowIsNull = true;Iterator<Cell> cellIterator = row.cellIterator();while (cellIterator.hasNext()) {Object cellValue = getCellValue(cellIterator.next());if (cellValue != null) {allRowIsNull = false;break;}}if (allRowIsNull) {LG.warn("Excel row " + row.getRowNum() + " all row value is null!");continue;}T t = null;StringBuilder log = new StringBuilder();if (clazz == Map.class) {Map<String, Object> map = new HashMap<String, Object>();for (String k : titleMap.keySet()) {Integer index = titleMap.get(k);String value = row.getCell(index).getStringCellValue();map.put(k, value);}list.add((T) map);} else {t = clazz.newInstance();int arrayIndex = 0;// 標(biāo)識當(dāng)前第幾個數(shù)組了int cellIndex = 0;// 標(biāo)識當(dāng)前讀到這一行的第幾個cell了List<FieldForSortting> fields = sortFieldByAnno(clazz);for (FieldForSortting ffs : fields) {Field field = ffs.getField();field.setAccessible(true);if (field.getType().isArray()) {Integer count = arrayCount[arrayIndex];Object[] value = null;if (field.getType().equals(String[].class)) {value = new String[count];} else {// 目前只支持String[]和Double[]value = new Double[count];}for (int i = 0; i < count; i++) {Cell cell = row.getCell(cellIndex);String errMsg = validateCell(cell, field, cellIndex);if (StringUtils.isBlank(errMsg)) {value[i] = getCellValue(cell);} else {log.append(errMsg);log.append(";");logs.setHasError(true);}cellIndex++;}field.set(t, value);arrayIndex++;} else {Cell cell = row.getCell(cellIndex);String errMsg = validateCell(cell, field, cellIndex);if (StringUtils.isBlank(errMsg)) {Object value = null;// 處理特殊情況,Excel中的String,轉(zhuǎn)換成Bean的Dateif (field.getType().equals(Date.class)&& cell.getCellType() == Cell.CELL_TYPE_STRING) {Object strDate = getCellValue(cell);try {value = new SimpleDateFormat(pattern).parse(strDate.toString());} catch (ParseException e) {errMsg =MessageFormat.format("the cell [{0}] can not be converted to a date ",CellReference.convertNumToColString(cell.getColumnIndex()));}} else {value = getCellValue(cell);// 處理特殊情況,excel的value為String,且bean中為其他,且defaultValue不為空,那就=defaultValueExcelCell annoCell = field.getAnnotation(ExcelCell.class);if (value instanceof String && !field.getType().equals(String.class)&& StringUtils.isNotBlank(annoCell.defaultValue())) {value = annoCell.defaultValue();}}field.set(t, value);}if (StringUtils.isNotBlank(errMsg)) {log.append(errMsg);log.append(";");logs.setHasError(true);}cellIndex++;}}list.add(t);logList.add(new ExcelLog(t, log.toString(), row.getRowNum() + 1));}}logs.setLogList(logList);} catch (InstantiationException e) {throw new RuntimeException(MessageFormat.format("can not instance class:{0}",clazz.getSimpleName()), e);} catch (IllegalAccessException e) {throw new RuntimeException(MessageFormat.format("can not instance class:{0}",clazz.getSimpleName()), e);}return list;}/*** 驗證Cell類型是否正確** @param cell cell單元格* @param field 欄位* @param cellNum 第幾個欄位,用於errMsg* @return*/private static String validateCell(Cell cell, Field field, int cellNum) {String columnName = CellReference.convertNumToColString(cellNum);String result = null;Integer[] integers = validateMap.get(field.getType());if (integers == null) {result = MessageFormat.format("Unsupported type [{0}]", field.getType().getSimpleName());return result;}ExcelCell annoCell = field.getAnnotation(ExcelCell.class);if (cell == null|| (cell.getCellType() == Cell.CELL_TYPE_STRING && StringUtils.isBlank(cell.getStringCellValue()))) {if (annoCell != null && annoCell.valid().allowNull() == false) {result = MessageFormat.format("the cell [{0}] can not null", columnName);};} else if (cell.getCellType() == Cell.CELL_TYPE_BLANK && annoCell.valid().allowNull()) {return result;} else {List<Integer> cellTypes = Arrays.asList(integers);// 如果類型不在指定範(fàn)圍內(nèi),並且沒有默認值if (!(cellTypes.contains(cell.getCellType()))|| StringUtils.isNotBlank(annoCell.defaultValue())&& cell.getCellType() == Cell.CELL_TYPE_STRING) {StringBuilder strType = new StringBuilder();for (int i = 0; i < cellTypes.size(); i++) {Integer intType = cellTypes.get(i);strType.append(getCellTypeByInt(intType));if (i != cellTypes.size() - 1) {strType.append(",");}}result =MessageFormat.format("the cell [{0}] type must [{1}]", columnName, strType.toString());} else {// 類型符合驗證,但值不在要求范圍內(nèi)的// String inif (annoCell.valid().in().length != 0 && cell.getCellType() == Cell.CELL_TYPE_STRING) {String[] in = annoCell.valid().in();String cellValue = cell.getStringCellValue();boolean isIn = false;for (String str : in) {if (str.equals(cellValue)) {isIn = true;}}if (!isIn) {result = MessageFormat.format("the cell [{0}] value must in {1}", columnName, in);}}// 數(shù)字型if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {double cellValue = cell.getNumericCellValue();// 小于if (!Double.isNaN(annoCell.valid().lt())) {if (!(cellValue < annoCell.valid().lt())) {result =MessageFormat.format("the cell [{0}] value must less than [{1}]", columnName,annoCell.valid().lt());}}// 大于if (!Double.isNaN(annoCell.valid().gt())) {if (!(cellValue > annoCell.valid().gt())) {result =MessageFormat.format("the cell [{0}] value must greater than [{1}]", columnName,annoCell.valid().gt());}}// 小于等于if (!Double.isNaN(annoCell.valid().le())) {if (!(cellValue <= annoCell.valid().le())) {result =MessageFormat.format("the cell [{0}] value must less than or equal [{1}]",columnName, annoCell.valid().le());}}// 大于等于if (!Double.isNaN(annoCell.valid().ge())) {if (!(cellValue >= annoCell.valid().ge())) {result =MessageFormat.format("the cell [{0}] value must greater than or equal [{1}]",columnName, annoCell.valid().ge());}}}}}return result;}/*** 根據(jù)annotation的seq排序后的欄位** @param clazz* @return*/private static List<FieldForSortting> sortFieldByAnno(Class<?> clazz) {Field[] fieldsArr = clazz.getDeclaredFields();List<FieldForSortting> fields = new ArrayList<FieldForSortting>();List<FieldForSortting> annoNullFields = new ArrayList<FieldForSortting>();for (Field field : fieldsArr) {ExcelCell ec = field.getAnnotation(ExcelCell.class);if (ec == null) {// 沒有ExcelCell Annotation 視為不匯入continue;}int id = ec.index();fields.add(new FieldForSortting(field, id));}fields.addAll(annoNullFields);sortByProperties(fields, true, false, "index");return fields;}@SuppressWarnings("unchecked")private static void sortByProperties(List<? extends Object> list, boolean isNullHigh,boolean isReversed, String... props) {if (CollectionUtils.isNotEmpty(list)) {Comparator<?> typeComp = ComparableComparator.getInstance();if (isNullHigh == true) {typeComp = ComparatorUtils.nullHighComparator(typeComp);} else {typeComp = ComparatorUtils.nullLowComparator(typeComp);}if (isReversed) {typeComp = ComparatorUtils.reversedComparator(typeComp);}List<Object> sortCols = new ArrayList<Object>();if (props != null) {for (String prop : props) {sortCols.add(new BeanComparator(prop, typeComp));}}if (sortCols.size() > 0) {Comparator<Object> sortChain = new ComparatorChain(sortCols);Collections.sort(list, sortChain);}}}}?
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/6858189.html
總結(jié)
- 上一篇: 唯品会API网关设计与实践--转
- 下一篇: 魅族大数据之流平台设计部署实践--转