2016年10月24日15:47:51 更新,發佈了最新版本,修改了一些BUG。另外在最後加上了調用的示例代碼java
package com.eya.util.office; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFFont; import org.apache.poi.hssf.usermodel.HSSFRichTextString; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.util.CellRangeAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.eya.util.ProDateUtil; import com.eya.util.reflects.ReflectUtils; /** * Excel工具類 * @create ll * @createDate 2016年4月14日 上午9:23:02 * @update * @updateDate */ public class PoiExcelUtils { /** 數字格式化 */ private static NumberFormat format = NumberFormat.getInstance(); /** 日誌 */ private static final Logger LOGGER = LoggerFactory.getLogger(PoiExcelUtils.class); /** 列默認寬度 */ private static final int DEFAUL_COLUMN_WIDTH = 4000; /** * 1.建立 workbook * @return {@link HSSFWorkbook} * @Author : ll. create at 2016年4月14日 上午9:28:27 */ private HSSFWorkbook getHSSFWorkbook() { LOGGER.info("【建立 workbook】"); return new HSSFWorkbook(); } /** * 2.建立 sheet * @param hssfWorkbook {@link HSSFWorkbook} * @param sheetName sheet 名稱 * @return {@link HSSFSheet} * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private HSSFSheet getHSSFSheet(HSSFWorkbook hssfWorkbook, String sheetName) { LOGGER.info("【建立 sheet】sheetName : " + sheetName); return hssfWorkbook.createSheet(sheetName); } /** * 3.寫入表頭信息 * @param hssfWorkbook {@link HSSFWorkbook} * @param hssfSheet {@link HSSFSheet} * @param headers 列標題,數組形式 * <p> * 如{"列標題1@beanFieldName1@columnWidth","列標題2@beanFieldName2@columnWidth","列標題3@beanFieldName3@columnWidth"} * </p> * <p> * 其中參數@columnWidth可選,columnWidth爲整型數值 * </p> * @param title 標題 * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private void writeHeader(HSSFWorkbook hssfWorkbook, HSSFSheet hssfSheet, String[] headers, String title) { LOGGER.info("【寫入表頭信息】"); // 頭信息處理 String[] newHeaders = headersHandler(headers); // 初始化標題和表頭單元格樣式 HSSFCellStyle titleCellStyle = createTitleCellStyle(hssfWorkbook); // 標題欄 HSSFRow titleRow = hssfSheet.createRow(0); titleRow.setHeight((short) 500); HSSFCell titleCell = titleRow.createCell(0); // 設置標題文本 titleCell.setCellValue(new HSSFRichTextString(title)); // 設置單元格樣式 titleCell.setCellStyle(titleCellStyle); // 處理單元格合併,四個參數分別是:起始行,終止行,起始行,終止列 hssfSheet.addMergedRegion(new CellRangeAddress(0, 0, (short) 0, (short) (newHeaders.length - 1))); // 設置合併後的單元格的樣式 titleRow.createCell(newHeaders.length - 1).setCellStyle(titleCellStyle); // 表頭 HSSFRow headRow = hssfSheet.createRow(1); headRow.setHeight((short) 500); HSSFCell headCell = null; String[] headInfo = null; // 處理excel表頭 for (int i = 0, len = newHeaders.length; i < len; i++) { headInfo = newHeaders[i].split("@"); headCell = headRow.createCell(i); headCell.setCellValue(headInfo[0]); headCell.setCellStyle(titleCellStyle); // 設置列寬度 setColumnWidth(i, headInfo, hssfSheet); } } /** * 寫入表頭信息 * @param hssfWorkbook {@link HSSFWorkbook} * @param hssfSheet {@link HSSFSheet} * @param headers 列標題,數組形式 * <p> * 如{"列標題1@beanFieldName1@columnWidth","列標題2@beanFieldName2@columnWidth","列標題3@beanFieldName3@columnWidth"} * </p> * <p> * 其中參數@columnWidth可選,columnWidth爲整型數值 * </p> * @param startIndex 起始行索引 * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private void writeHeader(HSSFWorkbook hssfWorkbook, HSSFSheet hssfSheet, String[] headers, int startIndex) { LOGGER.info("【寫入表頭信息】"); HSSFCellStyle headerCellStyle = createTitleCellStyle(hssfWorkbook); // 表頭 HSSFRow headRow = hssfSheet.createRow(startIndex); headRow.setHeight((short) 500); HSSFCell headCell = null; String[] headInfo = null; // 處理excel表頭 for (int i = 0, len = headers.length; i < len; i++) { headInfo = headers[i].split("@"); headCell = headRow.createCell(i); headCell.setCellValue(headInfo[0]); headCell.setCellStyle(headerCellStyle); // 設置列寬度 setColumnWidth(i, headInfo, hssfSheet); } } /** * 頭信息校驗和處理 * @param headers 列標題,數組形式 * <p> * 如{"列標題1@beanFieldName1@columnWidth","列標題2@beanFieldName2@columnWidth","列標題3@beanFieldName3@columnWidth"} * </p> * <p> * 其中參數@columnWidth可選,columnWidth爲整型數值 * </p> * @return 校驗後的頭信息 * @Author : ll. create at 2016年4月14日 上午9:24:48 */ private String[] headersHandler(String[] headers) { List<String> newHeaders = new ArrayList<String>(); for (String string : headers) { if (StringUtils.isNotBlank(string)) { newHeaders.add(string); } } int size = newHeaders.size(); return newHeaders.toArray(new String[size]); } /** * 4.寫入內容部分(默認從第三行開始寫入) * * @param hssfWorkbook {@link HSSFWorkbook} * @param hssfSheet {@link HSSFSheet} * @param headers 列標題,數組形式 * <p> * 如{"列標題1@beanFieldName1@columnWidth","列標題2@beanFieldName2@columnWidth","列標題3@beanFieldName3@columnWidth"} * </p> * <p> * 其中參數@columnWidth可選,columnWidth爲整型數值 * </p> * @param dataList 要導出的數據集合 * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private void writeContent(HSSFWorkbook hssfWorkbook, HSSFSheet hssfSheet, String[] headers, List<?> dataList) throws Exception { writeContent(hssfWorkbook, hssfSheet, headers, dataList, 2); } /** * 4.寫入內容部分 * * @param hssfWorkbook {@link HSSFWorkbook} * @param hssfSheet {@link HSSFSheet} * @param headers 列標題,數組形式 * <p> * 如{"列標題1@beanFieldName1@columnWidth","列標題2@beanFieldName2@columnWidth","列標題3@beanFieldName3@columnWidth"} * </p> * <p> * 其中參數@columnWidth可選,columnWidth爲整型數值 * </p> * @param dataList 要導出的數據集合 * @param startIndex 起始行的索引 * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private void writeContent(HSSFWorkbook hssfWorkbook, HSSFSheet hssfSheet, String[] headers, List<?> dataList, int startIndex) throws Exception { LOGGER.info("【寫入Excel內容部分】"); // 2015-8-13 增長,當沒有數據的時候,把原來拋異常的方式修改爲返回一個只有頭信息,沒有數據的空Excel if (CollectionUtils.isEmpty(dataList)) { LOGGER.warn("【沒有內容數據】"); return; } HSSFRow row = null; HSSFCell cell = null; // 單元格的值 Object cellValue = null; // 數據寫入行索引 int rownum = startIndex; // 單元格樣式 HSSFCellStyle cellStyle = createContentCellStyle(hssfWorkbook); // 遍歷集合,處理數據 for (int j = 0, size = dataList.size(); j < size; j++) { row = hssfSheet.createRow(rownum); for (int i = 0, len = headers.length; i < len; i++) { cell = row.createCell(i); cellValue = ReflectUtils.getValueOfGetIncludeObjectFeild(dataList.get(j), headers[i].split("@")[1]); cellValueHandler(cell, cellValue); cell.setCellStyle(cellStyle); } rownum++; } } /** * 設置列寬度 * @param i 列的索引號 * @param headInfo 表頭信息,其中包含了用戶須要設置的列寬 * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private void setColumnWidth(int i, String[] headInfo, HSSFSheet hssfSheet) { if (headInfo.length < 3) { // 用戶沒有設置列寬,使用默認寬度 hssfSheet.setColumnWidth(i, DEFAUL_COLUMN_WIDTH); return; } if (StringUtils.isBlank(headInfo[2])) { // 使用默認寬度 hssfSheet.setColumnWidth(i, DEFAUL_COLUMN_WIDTH); return; } // 使用用戶設置的列寬進行設置 hssfSheet.setColumnWidth(i, Integer.parseInt(headInfo[2])); } /** * 單元格寫值處理器 * @param {{@link HSSFCell} * @param cellValue 單元格值 * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private void cellValueHandler(HSSFCell cell, Object cellValue) { // 2015-8-13 修改,判斷cellValue是否爲空,不然在cellValue.toString()會出現空指針異常 if (cellValue == null) { cell.setCellValue(""); return; } if (cellValue instanceof String) { cell.setCellValue((String) cellValue); } else if (cellValue instanceof Boolean) { cell.setCellValue((Boolean) cellValue); } else if (cellValue instanceof Calendar) { cell.setCellValue((Calendar) cellValue); } else if (cellValue instanceof Double) { cell.setCellValue((Double) cellValue); } else if (cellValue instanceof Integer || cellValue instanceof Long || cellValue instanceof Short || cellValue instanceof Float) { cell.setCellValue((Double.parseDouble(cellValue.toString()))); } else if (cellValue instanceof HSSFRichTextString) { cell.setCellValue((HSSFRichTextString) cellValue); } cell.setCellValue(cellValue.toString()); } /** * 建立標題和表頭單元格樣式 * @param hssfWorkbook {@link HSSFWorkbook} * @return {@link HSSFCellStyle} * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private HSSFCellStyle createTitleCellStyle(HSSFWorkbook hssfWorkbook) { LOGGER.info("【建立標題和表頭單元格樣式】"); // 單元格的樣式 HSSFCellStyle cellStyle = hssfWorkbook.createCellStyle(); // 設置字體樣式,改成變粗 HSSFFont font = hssfWorkbook.createFont(); font.setFontHeightInPoints((short) 13); font.setBoldweight(Font.BOLDWEIGHT_BOLD); cellStyle.setFont(font); // 單元格垂直居中 cellStyle.setVerticalAlignment(HSSFCellStyle.ALIGN_CENTER_SELECTION); // 設置通用的單元格屬性 setCommonCellStyle(cellStyle); return cellStyle; } /** * 建立內容單元格樣式 * @param hssfWorkbook {@link HSSFWorkbook} * @return {@link HSSFCellStyle} * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private HSSFCellStyle createContentCellStyle(HSSFWorkbook hssfWorkbook) { LOGGER.info("【建立內容單元格樣式】"); // 單元格的樣式 HSSFCellStyle cellStyle = hssfWorkbook.createCellStyle(); // 設置字體樣式,改成不變粗 HSSFFont font = hssfWorkbook.createFont(); font.setFontHeightInPoints((short) 11); cellStyle.setFont(font); // 設置單元格自動換行 cellStyle.setWrapText(true); // 單元格垂直居中 cellStyle.setVerticalAlignment(HSSFCellStyle.ALIGN_CENTER_SELECTION); //水平居中 // cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 設置通用的單元格屬性 setCommonCellStyle(cellStyle); return cellStyle; } /** * 設置通用的單元格屬性 * @param cellStyle 要設置屬性的單元格 * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private void setCommonCellStyle(HSSFCellStyle cellStyle) { // 居中 cellStyle.setAlignment(CellStyle.ALIGN_CENTER); // 設置邊框 cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); } /** * 將生成的Excel輸出到指定目錄 * @param hssfWorkbook {@link HSSFWorkbook} * @param filePath 文件輸出目錄,包括文件名(.xls) * @Author : ll. create at 2016年4月14日 上午9:28:39 */ private void write2FilePath(HSSFWorkbook hssfWorkbook, String filePath) { LOGGER.info("【將生成的Excel輸出到指定目錄】filePath :" + filePath); FileOutputStream fileOut = null; try { fileOut = new FileOutputStream(filePath); hssfWorkbook.write(fileOut); } catch (Exception e) { LOGGER.error("【將生成的Excel輸出到指定目錄失敗】", e); throw new RuntimeException("將生成的Excel輸出到指定目錄失敗"); } finally { IOUtils.closeQuietly(fileOut); } } /** * 生成Excel,存放到指定目錄 * @param sheetName sheet名稱 * @param title 標題 * @param filePath 要導出的Excel存放的文件路徑 * @param headers 列標題,數組形式 * <p> * 如{"列標題1@beanFieldName1@columnWidth","列標題2@beanFieldName2@columnWidth","列標題3@beanFieldName3@columnWidth"} * </p> * <p> * 其中參數@columnWidth可選,columnWidth爲整型數值,默認4000 * </p> * @param dataList 要導出數據的集合 * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static void createExcel2FilePath(String sheetName, String title, String filePath, String[] headers, List<?> dataList) throws Exception { if (ArrayUtils.isEmpty(headers)) { LOGGER.warn("【表頭爲空】"); throw new RuntimeException("表頭不能爲空"); } LOGGER.info("【生成Excel,並存放到指定文件夾目錄下】sheetName : " + sheetName + " , title : " + title + " , filePath : " + filePath + " , headers : " + Arrays.toString(headers)); PoiExcelUtils poiExcelUtil = new PoiExcelUtils(); // 1.建立 Workbook HSSFWorkbook hssfWorkbook = poiExcelUtil.getHSSFWorkbook(); // 2.建立 Sheet HSSFSheet hssfSheet = poiExcelUtil.getHSSFSheet(hssfWorkbook, sheetName); // 3.寫入 head poiExcelUtil.writeHeader(hssfWorkbook, hssfSheet, headers, title); // 4.寫入內容 poiExcelUtil.writeContent(hssfWorkbook, hssfSheet, headers, dataList); // 5.保存文件到filePath中 poiExcelUtil.write2FilePath(hssfWorkbook, filePath); } /** * 生成Excel,存放到指定目錄 * @param sheetName sheet名稱 * @param title 標題 * @param filePath 要導出的Excel存放的文件路徑 * @param mainDataFields 主表數據須要展現的字段集合 * <p> * 如{"字段1@beanFieldName1","字段2@beanFieldName2",字段3@beanFieldName3"} * </p> * @param mainData 主表數據 * @param headers 列標題,數組形式 * <p> * 如{"列標題1@beanFieldName1@columnWidth","列標題2@beanFieldName2@columnWidth","列標題3@beanFieldName3@columnWidth"} * </p> * <p> * 其中參數@columnWidth可選,columnWidth爲整型數值,默認4000 * </p> * @param detailDataList 要導出數據的集合 * @param needExportDate 是否須要顯示「導出日期」 * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static void createExcel2FilePath(String sheetName, String title, String filePath, String[] mainDataFields, Object mainData, String[] headers, List<?> detailDataList, final boolean needExportDate) throws Exception { if (ArrayUtils.isEmpty(headers)) { LOGGER.warn("【參數headers爲空】"); throw new IllegalArgumentException("headers"); } if (ArrayUtils.isEmpty(mainDataFields)) { LOGGER.warn("【參數mainDataFields】"); throw new IllegalArgumentException("mainDataFields"); } if (mainData == null) { LOGGER.warn("【參數mainData】"); throw new IllegalArgumentException("mainData"); } LOGGER.info("【生成Excel,並存放到指定文件夾目錄下】sheetName : " + sheetName + " , title : " + title + " , filePath : " + filePath + " , headers : " + Arrays.toString(headers)); PoiExcelUtils poiExcelUtil = new PoiExcelUtils(); // 1.建立 Workbook HSSFWorkbook hssfWorkbook = poiExcelUtil.getHSSFWorkbook(); // 2.建立 Sheet HSSFSheet hssfSheet = poiExcelUtil.getHSSFSheet(hssfWorkbook, sheetName); // 3.寫標題 headers = poiExcelUtil.writeTitle(hssfWorkbook, hssfSheet, headers, title); // 4.寫主表(mainData)數據 int usedRows = poiExcelUtil.writeMainData(hssfWorkbook, hssfSheet, headers.length, mainDataFields, mainData, 1, needExportDate); // 5.寫入 head 這裏默認將title寫入到了第一行,因此header的起始行索引爲usedRows + 1 poiExcelUtil.writeHeader(hssfWorkbook, hssfSheet, headers, usedRows + 1); // 6.寫從表(detailDataList)內容 poiExcelUtil.writeContent(hssfWorkbook, hssfSheet, headers, detailDataList, usedRows + 2); // 7.保存文件到filePath中 poiExcelUtil.write2FilePath(hssfWorkbook, filePath); } /** * 寫標題 * @param hssfWorkbook {@link HSSFWorkbook} * @param hssfSheet {@link HSSFSheet} * @param headers 表頭 * @param title 標題 * @return 去除無效表頭後的新表頭集合 * @Author : ll. create at 2016年5月23日 上午11:24:08 */ private String[] writeTitle(HSSFWorkbook hssfWorkbook, HSSFSheet hssfSheet, String[] headers, String title) { return writeTitle(hssfWorkbook, hssfSheet, headers, title, 0); } /** * 寫標題 * @param hssfWorkbook {@link HSSFWorkbook} * @param hssfSheet {@link HSSFSheet} * @param headers 表頭 * @param title 標題 * @param titleRowIndex 標題行的索引 * @return 去除無效表頭後的新表頭集合 * @Author : ll. create at 2016年5月23日 上午11:24:08 */ private String[] writeTitle(HSSFWorkbook hssfWorkbook, HSSFSheet hssfSheet, String[] headers, String title, int titleRowIndex) { // 頭信息處理 String[] newHeaders = headersHandler(headers); // 初始化標題和表頭單元格樣式 HSSFCellStyle titleCellStyle = createTitleCellStyle(hssfWorkbook); // 標題欄 HSSFRow titleRow = hssfSheet.createRow(titleRowIndex); titleRow.setHeight((short) 500); HSSFCell titleCell = titleRow.createCell(0); // 設置標題文本 titleCell.setCellValue(new HSSFRichTextString(title)); // 設置單元格樣式 titleCell.setCellStyle(titleCellStyle); // 處理單元格合併,四個參數分別是:起始行,終止行,起始列,終止列 hssfSheet.addMergedRegion(new CellRangeAddress(titleRowIndex, titleRowIndex, (short) 0, (short) (newHeaders.length - 1))); // 設置合併後的單元格的樣式 titleRow.createCell(newHeaders.length - 1).setCellStyle(titleCellStyle); return newHeaders; } /** * 寫主表(mainData)數據 * @param hssfWorkbook {@link HSSFWorkbook} * @param hssfSheet {@link HSSFSheet} * @param columnSize 列數 * @param mainDataFields 主表數據須要展現的字段集合 * @param mainData 主表數據對象 * @param startIndex 起始行索引 * @param needExportDate 是否須要輸出「導出日期」 * @return 主表數據使用了多少行 * @throws Exception * @Author : ll. create at 2016年5月23日 上午10:24:58 */ private int writeMainData(HSSFWorkbook hssfWorkbook, HSSFSheet hssfSheet, int columnSize, String[] mainDataFields, Object mainData, int startIndex, boolean needExportDate) throws Exception { LOGGER.info("【寫主表(mainData)數據】columnSize = {} , mainDataFields = {} , mainData = {}", columnSize, Arrays.toString(mainDataFields), mainData); // 1.計算主表數據須要寫多少行,每行寫多少個單元格,每行寫多少個字段 int fieldsSize = mainDataFields.length; // 導出日期是否須要獨立一行顯示 boolean exportDateSingleRow = fieldsSize * 2 % columnSize == 0; // 主表屬性須要的行數 int needRows = exportDateSingleRow ? fieldsSize * 2 / columnSize : fieldsSize * 2 / columnSize + 1; if (needExportDate && exportDateSingleRow) { needRows += 1; } // 主表屬性顯示時,每行顯示的主表屬性量 int filedSizeInOneRow = fieldsSize * 2 < columnSize ? fieldsSize : columnSize / 2; // 列數是否爲偶數 final boolean isEvenColumn = columnSize % 2 == 0; // // 每一個字段須要2個單元格進行展現 --> fieldName : value // int fieldsSize = mainDataFields.length; // int needCellSize = needExportDate ? (fieldsSize + 1) * 2 : fieldsSize * 2; // // 轉換列數爲偶數 // final boolean isEvenColumn = columnSize % 2 == 0; // int availableColumns = isEvenColumn ? columnSize : columnSize - 1; // // 計算寫主表數據須要多少行 // int needRows = needCellSize % availableColumns == 0 ? needCellSize / availableColumns // : needCellSize / availableColumns + 1; // // 每行顯示的字段數 // int fieldsSizeAddExportDateCell = needExportDate ? fieldsSize + 1 : fieldsSize; // int filedSizeInOneRow = fieldsSizeAddExportDateCell % needRows == 0 ? fieldsSizeAddExportDateCell // / needRows // : fieldsSizeAddExportDateCell / needRows + 1; // 2.開始寫主表數據 HSSFRow row = null; HSSFCell cell4FiledName = null; HSSFCell cell4Value = null; // 數據寫入行索引 int rownum = startIndex; // 單元格樣式 HSSFCellStyle cellStyle = createContentCellStyle(hssfWorkbook); // 每一行的單元格的索引 int cellIndex = 0; // 主表字段的數組索引 int fieldIndex = 0; for (int i = 0; i < needRows; i++) { row = hssfSheet.createRow(rownum); for (int j = 0; j < filedSizeInOneRow; j++) { if (fieldIndex == fieldsSize) { break; } // 取出對應索引的主表字段,而後切割成字符串數組 String[] fieldsArray = mainDataFields[fieldIndex].split("@"); fieldIndex++; // 每一個字段對應的單元格的索引 cellIndex = j * 2; // 字段描述的單元格 cell4FiledName = row.createCell(cellIndex); cellValueHandler(cell4FiledName, fieldsArray[0]); cell4FiledName.setCellStyle(cellStyle); // 字段值的單元格 cell4Value = row.createCell(cellIndex + 1); cellValueHandler(cell4Value, ReflectUtils.getValueOfGetIncludeObjectFeild(mainData, fieldsArray[1])); cell4Value.setCellStyle(cellStyle); // 若是當前行還能夠繼續寫數據,則將導出日期寫在該行 if (fieldIndex == fieldsSize && needExportDate && filedSizeInOneRow != j + 1) { writeExportDate(hssfWorkbook, row, cellIndex + 2); needExportDate = false; hssfSheet.addMergedRegion(new CellRangeAddress(rownum, rownum, (short) cellIndex + 3, (short) (columnSize - 1))); // 設置合併後的單元格的樣式 setMergedCellStyle(row, cellIndex + 3, columnSize - 1, cellStyle); } // 若是最後一個有值的單元格後還有空白單元格,將他們合併 if ((j == filedSizeInOneRow - 1 && !isEvenColumn) || fieldIndex == fieldsSize) { int startCellIndex = needExportDate ? (short) cellIndex + 1 : (short) cellIndex + 3; // 處理單元格合併,四個參數分別是:起始行,終止行,起始列,終止列 hssfSheet.addMergedRegion(new CellRangeAddress(rownum, rownum, startCellIndex, (short) (columnSize - 1))); // 設置合併後的單元格的樣式 setMergedCellStyle(row, startCellIndex, columnSize - 1, cellStyle); } } // 導出日期獨自佔用一行 if (needExportDate && fieldIndex == fieldsSize) { int exportDateRowNum = rownum + 1; row = hssfSheet.createRow(exportDateRowNum); writeExportDate(hssfWorkbook, row, 0); hssfSheet.addMergedRegion(new CellRangeAddress(exportDateRowNum, exportDateRowNum, 1, (short) (columnSize - 1))); // 設置合併後的單元格的樣式 setMergedCellStyle(row, 1, columnSize - 1, cellStyle); // 生成一次導出日期以後,改變標識 needExportDate = false; break; } rownum++; } return needRows; } /** * 設置合併後的單元格的樣式 * @param row {@link HSSFRow} * @param beginCellIdnex 合併開始的單元格 * @param endCellIndex 合併結束的單元格 * @param cellStyle {@link HSSFCellStyle} * @Author : ll. create at 2016年5月23日 下午5:32:12 */ private void setMergedCellStyle(HSSFRow row, int beginCellIdnex, int endCellIndex, HSSFCellStyle cellStyle) { for (int i = beginCellIdnex + 1; i <= endCellIndex; i++) { row.createCell(i).setCellStyle(cellStyle); } } /** * 寫入導出日期 * @param row {@link HSSFRow} * @param cellIndex 列索引 * @Author : ll. create at 2016年5月23日 下午3:13:41 */ private void writeExportDate(HSSFWorkbook hssfWorkbook, HSSFRow row, int cellIndex) { // 單元格樣式 HSSFCellStyle cellStyle = createContentCellStyle(hssfWorkbook); // 導出日期 HSSFCell cell4ExortDate = row.createCell(cellIndex); cellValueHandler(cell4ExortDate, "導出日期"); cell4ExortDate.setCellStyle(cellStyle); // 導出日期的值 HSSFCell cell4ExportDateValue = row.createCell(cellIndex + 1); cellValueHandler(cell4ExportDateValue, ProDateUtil.getCurrentDate(ProDateUtil.DATE_TIME_FORMAT)); cell4ExportDateValue.setCellStyle(cellStyle); } /** * 生成Excel的WorkBook,用於導出Excel * @param sheetName sheet名稱 * @param title 標題 * @param headers 列標題,數組形式 * <p> * 如{"列標題1@beanFieldName1@columnWidth","列標題2@beanFieldName2@columnWidth","列標題3@beanFieldName3@columnWidth"} * </p> * <p> * 其中參數@columnWidth可選,columnWidth爲整型數值,默認4000 * </p> * @param dataList 要導出數據的集合 * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static HSSFWorkbook createExcel2Export(String sheetName, String title, String[] headers, List<?> dataList) throws Exception { if (ArrayUtils.isEmpty(headers)) { LOGGER.warn("【表頭爲空】"); throw new RuntimeException("表頭不能爲空"); } LOGGER.info("【生成Excel的WorkBook,用於導出Excel】sheetName : " + sheetName + " , title : " + title + " , headers : " + Arrays.toString(headers)); PoiExcelUtils poiExcelUtil = new PoiExcelUtils(); // 1.建立 Workbook HSSFWorkbook hssfWorkbook = poiExcelUtil.getHSSFWorkbook(); // 2.建立 Sheet HSSFSheet hssfSheet = poiExcelUtil.getHSSFSheet(hssfWorkbook, sheetName); // 3.寫入 head poiExcelUtil.writeHeader(hssfWorkbook, hssfSheet, headers, title); // 4.寫入內容 poiExcelUtil.writeContent(hssfWorkbook, hssfSheet, headers, dataList); return hssfWorkbook; } /** * 生成Excel的WorkBook,用於導出Excel * @param sheetName sheet名稱 * @param title 標題 * @param mainDataFields 主表數據須要展現的字段集合 * <p> * 如{"字段1@beanFieldName1","字段2@beanFieldName2",字段3@beanFieldName3"} * </p> * @param mainData 主表數據 * @param headers 列標題,數組形式 * <p> * 如{"列標題1@beanFieldName1@columnWidth","列標題2@beanFieldName2@columnWidth","列標題3@beanFieldName3@columnWidth"} * </p> * <p> * 其中參數@columnWidth可選,columnWidth爲整型數值,默認4000 * </p> * @param detailDataList 要導出數據的集合 * @param needExportDate 是否須要「導出日期」 * @return {@link HSSFWorkbook} * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static HSSFWorkbook createExcel2Export(String sheetName, String title, String[] mainDataFields, Object mainData, String[] headers, List<?> detailDataList, boolean needExportDate) throws Exception { if (ArrayUtils.isEmpty(headers)) { LOGGER.warn("【參數headers爲空】"); throw new IllegalArgumentException("headers"); } if (ArrayUtils.isEmpty(mainDataFields)) { LOGGER.warn("【參數mainDataFields】"); throw new IllegalArgumentException("mainDataFields"); } if (mainData == null) { LOGGER.warn("【參數mainData】"); throw new IllegalArgumentException("mainData"); } LOGGER.info("【生成Excel,用於導出】sheetName : " + sheetName + " , title : " + title + " , headers : " + Arrays.toString(headers) + " , mainDataFields = " + Arrays.toString(mainDataFields)); PoiExcelUtils poiExcelUtil = new PoiExcelUtils(); // 1.建立 Workbook HSSFWorkbook hssfWorkbook = poiExcelUtil.getHSSFWorkbook(); // 2.建立 Sheet HSSFSheet hssfSheet = poiExcelUtil.getHSSFSheet(hssfWorkbook, sheetName); // 3.寫標題 headers = poiExcelUtil.writeTitle(hssfWorkbook, hssfSheet, headers, title); // 4.寫主表(mainData)數據 int usedRows = poiExcelUtil.writeMainData(hssfWorkbook, hssfSheet, headers.length, mainDataFields, mainData, 1, needExportDate); // 5.寫入 head 這裏默認將title寫入到了第一行,而後須要header和主表詳情間隔一行 poiExcelUtil.writeHeader(hssfWorkbook, hssfSheet, headers, usedRows + 2); // 6.寫從表(detailDataList)內容 poiExcelUtil.writeContent(hssfWorkbook, hssfSheet, headers, detailDataList, usedRows + 3); return hssfWorkbook; } /** * 根據文件路徑讀取excel文件,默認讀取第0個sheet * @param excelPath excel的路徑 * @param skipRows 須要跳過的行數 * @param columnCount 列數量 * @return List<String[]> 集合中每個元素是一個數組,按單元格索引存儲每一個單元格的值,一個元素能夠封裝成一個須要的java bean * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static List<String[]> readExcel(String excelPath, int skipRows, int columnCount) throws Exception { return readExcel(excelPath, skipRows, columnCount, 0, null); } /** * 根據文件路徑讀取excel文件的指定sheet * @param excelPath excel的路徑 * @param skipRows 須要跳過的行數 * @param columnCount 列數量 * @param sheetNo 要讀取的sheet的索引,從0開始 * @return List<String[]> 集合中每個元素是一個數組,按單元格索引存儲每一個單元格的值,一個元素能夠封裝成一個須要的java bean * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static List<String[]> readExcel(String excelPath, int skipRows, int columnCount, int sheetNo) throws Exception { return readExcel(excelPath, skipRows, columnCount, sheetNo, null); } /** * 根據文件路徑讀取excel文件的指定sheet,並封裝空值單位各的座標,默認讀取第0個sheet * @param excelPath excel的路徑 * @param skipRows 須要跳過的行數 * @param columnCount 列數量 * @param noneCellValuePositionList 存儲空值的單元格的座標,每一個座標以x-y的形式拼接,如2-5表示第二行第五列 * @return List<String[]> 集合中每個元素是一個數組,按單元格索引存儲每一個單元格的值,一個元素能夠封裝成一個須要的java bean * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static List<String[]> readExcel(String excelPath, int skipRows, int columnCount, List<String> noneCellValuePositionList) throws Exception { return readExcel(excelPath, skipRows, columnCount, 0, noneCellValuePositionList); } /** * 根據文件路徑讀取excel文件的指定sheet,並封裝空值單位各的座標,默認讀取第0個sheet * @param excelPath excel的路徑 * @param skipRows 須要跳過的行數 * @param columnCount 列數量 * @param columnNumberForSkipValueValidateSet 不須要作空值驗證的列的索引集合 * @param noneCellValuePositionList 存儲空值的單元格的座標,每一個座標以x-y的形式拼接,如2-5表示第二行第五列 * @return List<String[]> 集合中每個元素是一個數組,按單元格索引存儲每一個單元格的值,一個元素能夠封裝成一個須要的java bean * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static List<String[]> readExcel(String excelPath, int skipRows, int columnCount, Set<Integer> columnNumberForSkipValueValidateSet, List<String> noneCellValuePositionList) throws Exception { return readExcel(excelPath, skipRows, columnCount, 0, columnNumberForSkipValueValidateSet, noneCellValuePositionList); } /** * 根據文件路徑讀取excel文件的指定sheet,並封裝空值單位各的座標 * @param excelPath excel的路徑 * @param skipRows 須要跳過的行數 * @param columnCount 列數量 * @param sheetNo 要讀取的sheet的索引,從0開始 * @param noneCellValuePositionList 存儲空值的單元格的座標,每一個座標以x-y的形式拼接,如2-5表示第二行第五列 * @return List<String[]> 集合中每個元素是一個數組,按單元格索引存儲每一個單元格的值,一個元素能夠封裝成一個須要的java bean * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static List<String[]> readExcel(String excelPath, int skipRows, int columnCount, int sheetNo, List<String> noneCellValuePositionList) throws Exception { return readExcel(excelPath, skipRows, columnCount, sheetNo, null, noneCellValuePositionList); } /** * 根據文件路徑讀取excel文件的指定sheet,並封裝空值單位各的座標 * @param excelPath excel的路徑 * @param skipRows 須要跳過的行數 * @param columnCount 列數量 * @param sheetNo 要讀取的sheet的索引,從0開始 * @param columnNumberForSkipValueValidateSet 不須要作空值驗證的列的索引集合 * @param noneCellValuePositionList 存儲空值的單元格的座標,每一個座標以x-y的形式拼接,如2-5表示第二行第五列 * @return List<String[]> 集合中每個元素是一個數組,按單元格索引存儲每一個單元格的值,一個元素能夠封裝成一個須要的java bean * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:28:39 */ public static List<String[]> readExcel(String excelPath, int skipRows, int columnCount, int sheetNo, Set<Integer> columnNumberForSkipValueValidateSet, List<String> noneCellValuePositionList) throws Exception { LOGGER .info( "【讀取Excel】excelPath = {} , skipRows = {} , columnCount = {} , columnNumberForSkipValueValidateSet = {}", excelPath, skipRows, columnCount, columnNumberForSkipValueValidateSet); if (StringUtils.isBlank(excelPath)) { LOGGER.warn("【參數excelPath爲空】"); return new ArrayList<String[]>(); } FileInputStream is = new FileInputStream(new File(excelPath)); POIFSFileSystem fs = new POIFSFileSystem(is); HSSFWorkbook wb = new HSSFWorkbook(fs); List<String[]> list = new ArrayList<String[]>(); HSSFSheet sheet = wb.getSheetAt(sheetNo); // 獲得總共的行數 int rowNum = sheet.getPhysicalNumberOfRows(); try { for (int i = skipRows; i < rowNum; i++) { String[] vals = new String[columnCount]; HSSFRow row = sheet.getRow(i); if (null == row) { continue; } for (int j = 0; j < columnCount; j++) { HSSFCell cell = row.getCell(j); String val = getStringCellValue(cell); // 沒有須要跳過校驗的列索引 if (CollectionUtils.isEmpty(columnNumberForSkipValueValidateSet)) { if (noneCellValuePositionList != null && StringUtils.isBlank(val)) { // 封裝空值單元格的座標 noneCellValuePositionList.add((i + 1) + "-" + j); } } else { // 若是須要校驗空值的單元格、當前列索引不在須要跳過校驗的索引集合中 if (noneCellValuePositionList != null && StringUtils.isBlank(val) && !columnNumberForSkipValueValidateSet.contains(j)) { // 封裝空值單元格的座標 noneCellValuePositionList.add((i + 1) + "-" + j); } } vals[j] = val; } list.add(vals); } } catch (Exception e) { LOGGER.error("【Excel解析失敗】", e); throw new RuntimeException("Excel解析失敗"); } finally { wb.close(); } return list; } /** * 獲取單元格數據內容爲字符串類型的數據 * @param cell Excel單元格{@link HSSFCell} * @return 單元格數據內容(多是布爾類型等,強制轉換成String) * @Author : ll. create at 2016年4月14日 上午9:53:07 */ private static String getStringCellValue(HSSFCell cell) { if (cell == null) { return ""; } String strCell = ""; switch (cell.getCellType()) { case HSSFCell.CELL_TYPE_STRING: strCell = cell.getStringCellValue(); break; case HSSFCell.CELL_TYPE_NUMERIC: strCell = String.valueOf(format.format(cell.getNumericCellValue())) .replace(",", ""); break; case HSSFCell.CELL_TYPE_BOOLEAN: strCell = String.valueOf(cell.getBooleanCellValue()); break; case HSSFCell.CELL_TYPE_BLANK: strCell = ""; break; default: strCell = ""; break; } if (StringUtils.isBlank(strCell)) { return ""; } return strCell; } }
2016年10月24日15:49:33更新:補充了反射工具類ReflectUtils.javaapache
package com.eya.util.reflects; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.commons.lang3.StringUtils; import com.eya.util.ProString; /** * 反射工具類 * @create ll * @createDate 2016年4月14日 上午9:17:51 * @update * @updateDate */ public class ReflectUtils { /** * 經過字段名從對象或對象的父類中獲得字段的值 * @param object 對象實例 * @param fieldName 字段名 * @return 字段對應的值 * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:18:19 */ public static Object getValue(Object object, String fieldName) throws Exception { if (object == null) { return null; } if (StringUtils.isBlank(fieldName)) { return null; } Field field = null; Class<?> clazz = object.getClass(); for (; clazz != Object.class; clazz = clazz.getSuperclass()) { try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); return field.get(object); } catch (Exception e) { //這裏甚麼都不要作!而且這裏的異常必須這樣寫,不能拋出去。 //若是這裏的異常打印或者往外拋,則就不會執行clazz = clazz.getSuperclass(),最後就不會進入到父類中了 } } return null; } /** * 經過字段名從對象或對象的父類中獲得字段的值(調用字典的get方法) * @param object 對象實例 * @param fieldName 字段名 * @return 字段對應的值 * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:18:19 */ public static Object getValueOfGet(Object object, String fieldName) throws Exception { if (object == null) { return null; } if (StringUtils.isBlank(fieldName)) { return null; } Field field = null; Class<?> clazz = object.getClass(); for (; clazz != Object.class; clazz = clazz.getSuperclass()) { try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz); //得到get方法 Method getMethod = pd.getReadMethod(); //執行get方法返回一個Object return getMethod.invoke(object); } catch (Exception e) { //這裏甚麼都不要作!而且這裏的異常必須這樣寫,不能拋出去。 //若是這裏的異常打印或者往外拋,則就不會執行clazz = clazz.getSuperclass(),最後就不會進入到父類中了 } } return null; } /** * 經過字段名從對象或對象的父類中獲得字段的值(調用字典的get方法,能夠取出複雜的對象的值) * @param object 對象實例 * @param fieldName 字段名 * @return 字段對應的值 * @throws Exception * @Author : ll. create at 2016年4月14日 上午9:18:19 */ public static Object getValueOfGetIncludeObjectFeild(Object object, String fieldName) throws Exception { if (object == null) { return null; } if (StringUtils.isBlank(fieldName)) { return null; } Field field = null; Class<?> clazz = object.getClass(); for (; clazz != Object.class; clazz = clazz.getSuperclass()) { try { if (fieldName.contains(".")) { // 如:operatorUser.name、operatorUser.org.name,遞歸調用 String[] splitFiledName = fieldName.split("\\."); return getValueOfGetIncludeObjectFeild( getValueOfGetIncludeObjectFeild(object, splitFiledName[0]), splitFiledName[1]); } field = clazz.getDeclaredField(fieldName); field.setAccessible(true); PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz); //得到get方法 Method getMethod = pd.getReadMethod(); //執行get方法返回一個Object return getMethod.invoke(object); } catch (Exception e) { //這裏甚麼都不要作!而且這裏的異常必須這樣寫,不能拋出去。 //若是這裏的異常打印或者往外拋,則就不會執行clazz = clazz.getSuperclass(),最後就不會進入到父類中了 } } return null; } }
2016年10月27日09:12:27 更新數組
調用代碼,分爲兩種,一種是主從表形式,如一個採購訂單,有訂單的主信息,如採購人,採購時間等,從表信息是採購的商品明細信息。另外一種是簡單的表格數據,如導出系統全部的用戶信息等。app
示例一:主從表形式(service的實現)ide
/** * 生成用於導出Excel的數據 * @param id 到貨單ID * @return {@link HSSFWorkbook} * @Author : ll. create at 2016年10月24日 下午2:02:40 */ public HSSFWorkbook exportExcel(String id) { logger.info("【生成須要導出的到貨單Excel表格數據】id = {}" + id); if (ProString.isBlank(id)) { logger.warn("【參數id爲空】"); throw new BusinessException(LangResource.get( ErrorEnum.COMMON_NULL_ARGUMENT.getDescCode(), "id")); } // 查詢主表數據 ArrivalOrderView arrivalOrderView = this.detail(id); // 查詢從表數據 List<ArrivalOrderDetailView> detailList = arrivalOrderDetailService.selectList((id); // 配置主表數據須要顯示的字段 String[] mainDataFields = { "到貨單號@id", "採購單號@billNum", "供應商@orgIdExportValue", "狀態@statusValue", "倉庫@depotIdValue", "經手人@userIdOperatorValue", "到貨日期@arriveDateValue", "聯繫人@contacts", "聯繫電話@contactPhone", "審覈人@checkByValue", "審覈日期@checkDateValue", "建立人@createByValue", "建立日期@createDateValue", "備註@remark" }; // 配置明細表須要顯示的字段 String[] headers = { "產品代碼@reagentCode", "產品名稱@reagentName@6000", "生產廠家@brandName", "規格@standard", "註冊證號@licenseNumber@5000", "註冊證有效起始日期@licenseBeginDateValue@6000", "註冊證失效日期@licenseEndDateValue@6000", "生產批號@storeBatchNumber", "生產日期@productionDateValue", "失效日期@effectiveEndDateValue", "數量@qty", "單位@unitName", "合格標誌@qualifiedMarkValue", "不合格緣由@invalidReason@8000" }; try { return PoiExcelUtils.createExcel2Export("到貨單明細信息", "到貨單明細信息", mainDataFields, arrivalOrderView, headers, detailList, true); } catch (Exception e) { logger.error("【生成Excel表格數據失敗】", e); throw new RuntimeException("生成Excel表格數據失敗"); } }
導出的效果工具
示例二:簡單的表格數據(service的實現)字體
/** * 生成須要導出的Excel表格數據 * @param id 須要導出的明細數據 * @return {@link HSSFWorkbook} * @Author : ll. create at 2016年6月30日 下午12:00:39 */ public HSSFWorkbook exportExcel(List<PurchOrderDetailQueryView> purchOrderDetailQueryViewList) { logger.info("【生成須要導出的Excel表格數據】purchOrderDetailQueryViewList = {}", purchOrderDetailQueryViewList); String[] headers = { "採購日期@purchDate@8000", "採購單號@purchNum@8000", "試劑代碼@reagentCode", "試劑名稱@reagentName@13000", "生產廠家@brand", "規格@standard@7000", "供應商@orgIdProvider", "數量@qty", "單位@unitName", "單價@price", "主單位數量@qtyOfMainUnit", "主單位@mainUnitName", "金額@amt" }; try { return PoiExcelUtils.createExcel2Export("採購明細", "採購明細", headers, purchOrderDetailQueryViewList); } catch (Exception e) { logger.error("【生成Excel表格數據失敗】", e); throw new RuntimeException("生成Excel表格數據失敗", e); } }
導出的效果ui
這裏再提供一下Controller的實現,只提供一個就能夠,由於都是相似的。this
/** * 導出到貨單Excel * @param request {@link HttpServletRequest} * @param response {@link HttpServletResponse} * @param id 到貨單的id * @Author : ll. create at 2016年6月1日 下午4:48:44 */ @RequestMapping(value = "exportExcel.do", method = RequestMethod.GET) public void exportExcel(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "id", required = true) String id) { logger.info("【導出到貨單Excel】id = {}", id); init(request); try { HSSFWorkbook hssfWorkbook = arrivalOrderService.exportExcel(id); writeWorkbook(response, hssfWorkbook); } catch (Exception e) { logger.error("【導出到貨單Excel失敗】", e); throw new ForwardErrorPageException("導出到貨單Excel失敗"); } } /** * 導出Excel數據表格 * @param response {@link HttpServletResponse} * @param workbook {@link HSSFWorkbook} * @Author : ll. create at 2016年5月21日 上午11:12:18 */ protected void writeWorkbook(HttpServletResponse response, HSSFWorkbook workbook) { response.reset(); response.setCharacterEncoding("utf-8"); response.setContentType("multipart/form-data"); String fileName = System.currentTimeMillis() + ".xls"; response.setHeader("Content-Disposition", "attachment;fileName=" + fileName); OutputStream out = null; try { out = response.getOutputStream(); workbook.write(out); workbook.close(); } catch (Exception e) { logger.error("【導出失敗】", e); } finally { // 使用的是org.apache.commons.io.IOUtils IOUtils.closeQuietly(out); } }