java POI解析excel、导出及合并excel

一、引入poi包

<!-- Excel 依赖包-->
<dependency>
   <groupId>org.apache.poi</groupId>
   <artifactId>poi</artifactId>
   <version>3.17</version>
</dependency>

<dependency>
   <groupId>org.apache.poi</groupId>
   <artifactId>poi-ooxml</artifactId>
   <version>3.17</version>
</dependency>

poi包用于处理xls格式,poi-ooxml用户处理xlsx格式。

二、代码


public class ExcelUtils {

    public static void exportExcel(HttpServletResponse response, String fileName, ExcelData data, Class cls) throws Exception {
        // 告诉浏览器用什么软件可以打开此文件
        response.setHeader("content-Type", "application/vnd.ms-excel");
        // 下载文件的默认名称
        response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(fileName, "utf-8"));
        exportExcel(data, response.getOutputStream(), cls);
    }

    public static void exportExcel(ExcelData data, OutputStream out, Class cls) throws Exception {

        XSSFWorkbook wb = new XSSFWorkbook();
        try {
            String sheetName = data.getName();
            if (null == sheetName) {
                sheetName = "Sheet1";
            }
            XSSFSheet sheet = wb.createSheet(sheetName);
            writeExcel(wb, sheet, data, cls);

            wb.write(out);
        } finally {
            wb.close();
        }
    }

    private static void writeExcel(XSSFWorkbook wb, Sheet sheet, ExcelData data, Class cls) {

        int rowIndex = 0;

        rowIndex = writeTitlesToExcel(wb, sheet, data.getTitles(), cls);
        writeRowsToExcel(wb, sheet, data.getRows(), rowIndex, cls);
        mergeCell(sheet, data.getRows(), cls);//合并单元格
        autoSizeColumns(sheet, data.getTitles().size() + 1);

    }

    private static int writeTitlesToExcel(XSSFWorkbook wb, Sheet sheet, List<String> titles, Class cls) {
        int rowIndex = 0;
        int colIndex = 0;

        // 如果没有标题,则不写标题
        if(titles == null || titles.size() <= 0){
            return rowIndex;
        }

        Font titleFont = wb.createFont();
        titleFont.setFontName("simsun");
        titleFont.setBold(true);
        // titleFont.setFontHeightInPoints((short) 14);
        titleFont.setColor(IndexedColors.BLACK.index);

        XSSFCellStyle titleStyle = wb.createCellStyle();
        titleStyle.setAlignment(HorizontalAlignment.CENTER);
        titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        titleStyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(182, 184, 192)));
        titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        titleStyle.setFont(titleFont);
        setBorder(titleStyle, BorderStyle.THIN, new XSSFColor(new java.awt.Color(0, 0, 0)));

        Row titleRow = sheet.createRow(rowIndex);
        // titleRow.setHeightInPoints(25);
        colIndex = 0;

        Field[] fields = cls.getDeclaredFields();
        for(Field field : fields){
            String cellTile = field.getName();
            if (null!=field.getAnnotation(ColumnName.class)) {
                cellTile = field.getAnnotation(ColumnName.class).value();
            }

            Cell cell = titleRow.createCell(colIndex);
            cell.setCellValue(cellTile);
            cell.setCellStyle(titleStyle);
            colIndex++;
        }

        rowIndex++;
        return rowIndex;
    }

    private static int writeRowsToExcel(XSSFWorkbook wb, Sheet sheet, List rows, int rowIndex, Class cls) {
        try{
            int colIndex = 0;

            Font dataFont = wb.createFont();
            dataFont.setFontName("simsun");
            // dataFont.setFontHeightInPoints((short) 14);
            dataFont.setColor(IndexedColors.BLACK.index);

            XSSFCellStyle dataStyle = wb.createCellStyle();
            dataStyle.setAlignment(HorizontalAlignment.CENTER);
            dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            dataStyle.setFont(dataFont);
            setBorder(dataStyle, BorderStyle.THIN, new XSSFColor(new java.awt.Color(0, 0, 0)));

            Map<String, Method> methods = getReadMethods(cls);
            Field[] fields = cls.getDeclaredFields();
            Map<String, Class<?>> types = getTypes(cls);

            for (Object rowData : rows) {
                Row dataRow = sheet.createRow(rowIndex);
                // dataRow.setHeightInPoints(25);
                colIndex = 0;

                for (Field field : fields) {
                    Method m = methods.get(field.getName());
                    Class<?> type = types.get(field.getName());
                    try {
                        Object cellData = m.invoke(rowData);

                        Cell cell = dataRow.createCell(colIndex);
                        if (cellData != null) {
                            setCellValue(cell, type, cellData);
                        } else {
                            cell.setCellValue("");
                        }

                        cell.setCellStyle(dataStyle);
                        colIndex++;

                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }

                rowIndex++;
            }
        } catch (Exception e){
            e.printStackTrace();
        }

        return rowIndex;
    }

    private static void mergeCell(Sheet sheet, List rows, Class cls) {
        try{
            List<DataExport> exportDataList = rows;

			int[] mergeCol = {0,1,2,3,4,5,6}; //要合并的列,从0开始

			//自动合并
			for (int k = 0; k < mergeCol.length; k++) {
				int colNum = mergeCol[k]; //要合并的列
				int d = 0; //下次要合并的行
				int n = exportDataList.size()-1; //当前合并的行
				for (int i = exportDataList.size()-1; i >= 1; i--) { //每一行
					if(i!=(n-d)) {
						continue;
					}
					String serialNumber = exportDataList.get(i).getSerialNumber();
					serialNumber = (serialNumber == null ? "" : serialNumber);
					String lastSerialNumber = exportDataList.get(i-1).getSerialNumber();
					lastSerialNumber = (lastSerialNumber == null ? "" : lastSerialNumber);
					if(serialNumber.equals(lastSerialNumber)){ //相邻的上下两行

						int rowspan = 2;
						d = rowspan;
						for (int j = 2; i-j >= 0; j++) { //判断上下多行内容一样
							n = i;
							String tmpSerialNumber = exportDataList.get(i-j).getSerialNumber();
							tmpSerialNumber = (tmpSerialNumber == null ? "" : tmpSerialNumber);
							if(!serialNumber.equals(tmpSerialNumber)){
								break;
							}else{
								rowspan = j+1;
								d = rowspan;
							}
						}
						// 合并单元格时,只保留一行数据,其它数据清空,否则数据求和时影响数据计算
						int startRow = i-rowspan+2;//合并起始行
						int endRow = i+1;//合并结束行
						for(int m = startRow + 1; m <= endRow; m++){
							Row row = sheet.getRow(m);
							row.getCell(k).setCellValue("");
						}

						sheet.addMergedRegion(new CellRangeAddress(i-rowspan+2, i+1, colNum, colNum));
					}else {
						d = 0;
						n = i-1;
					}

				}
			}

        } catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 根据字段类型调用不同的写数据方法
     * @param cell--写的行
     * @param type--字段类型
     * @param cellData--数据
     */
    private static void setCellValue(Cell cell, Class<?> type, Object cellData){
        if(type == String.class){
            cell.setCellValue((String)cellData);
        } else if (type == Integer.class || type == int.class) {
            cell.setCellValue((Integer)cellData);
        } else if (type == Long.class || type == long.class) {
            cell.setCellValue((Long)cellData);
        } else if (type == Float.class || type == float.class) {
            cell.setCellValue((Float)cellData);
        } else if (type == Double.class || type == double.class) {
            cell.setCellValue((Double)cellData);
        } else if (type == boolean.class || type == Boolean.class) {
            cell.setCellValue((Boolean)cellData);
        } else if (type == Date.class ) {
            cell.setCellValue((Date)cellData);
        } else if (type == BigDecimal.class ) {
            if(cellData != null){
                cell.setCellValue(((BigDecimal)cellData).doubleValue());
            } else {
                cell.setCellValue("");
            }
        } else {
            cell.setCellValue(JSON.toJSONString(cellData));
        }

    }

	// 设置列宽自适应
    private static void autoSizeColumns(Sheet sheet, int columnNumber) {

        for (int i = 0; i < columnNumber; i++) {
            int orgWidth = sheet.getColumnWidth(i);
            sheet.autoSizeColumn(i, true);
            int newWidth = (int) (sheet.getColumnWidth(i) + 100);
            if (newWidth > orgWidth) {
                sheet.setColumnWidth(i, newWidth);
            } else {
                sheet.setColumnWidth(i, orgWidth);
            }
        }
    }

    private static void setBorder(XSSFCellStyle style, BorderStyle border, XSSFColor color) {
        style.setBorderTop(border);
        style.setBorderLeft(border);
        style.setBorderRight(border);
        style.setBorderBottom(border);
        style.setBorderColor(XSSFCellBorder.BorderSide.TOP, color);
        style.setBorderColor(XSSFCellBorder.BorderSide.LEFT, color);
        style.setBorderColor(XSSFCellBorder.BorderSide.RIGHT, color);
        style.setBorderColor(XSSFCellBorder.BorderSide.BOTTOM, color);
    }

    /**
     *
     * @param cls
     * @return
     */
   private static PropertyDescriptor[] getPropertyDescriptor(Class  cls) {
        PropertyDescriptor[] props = new PropertyDescriptor[0];
        try {
            BeanInfo beaninfo = Introspector.getBeanInfo(cls);
            props = beaninfo.getPropertyDescriptors();
        } catch (IntrospectionException e) {
        }
        return props;

   }
   /**
    * 获取写方法
    * @param cls
    * @return 写方法map
    */
    private static Map<String, Method> getWriteMethods(Class cls) {
        Map<String, Method> methods = new HashMap<String, Method>();
        PropertyDescriptor[] props = getPropertyDescriptor(cls);
        for (int i = 0; i < props.length; i++) {
            Method sm = props[i].getWriteMethod();
            if (sm != null) {
                String field = props[i].getName();
                methods.put(field, sm);
            }
        }
        return methods;
    }


    /**
     * 获取读方法
     * @param cls
     * @return 读方法map
     */
    public static Map<String, Method> getReadMethods(Class cls) {
        Map<String, Method> methods = new HashMap<String, Method>();
        PropertyDescriptor[] props = getPropertyDescriptor(cls);
        for (int i = 0; i < props.length; i++) {
            Method sm = props[i].getReadMethod();
            if (sm != null) {
                String field = props[i].getName();
                methods.put(field, sm);
            }
        }
        return methods;
    }

    /**
     * 获取类型
     * @param cls
     * @return 类型map
     */
    private static Map<String, Class<?>> getTypes(Class cls) {
        Map<String, Class<?>> types = new HashMap<String, Class<?>>();
        PropertyDescriptor[] props = getPropertyDescriptor(cls);
        for (int i = 0; i < props.length; i++) {
            Class<?> type = props[i].getPropertyType();
            if (types != null) {
                String field = props[i].getName();
                types.put(field, type);
            }
        }
        return types;
    }

    public static boolean isExcel2003(String fileName){
        if(fileName == null){
            fileName = "";
        }
        boolean isExcel2003 = fileName.toLowerCase().endsWith("xls") ? true : false;
        return isExcel2003;
    }
    public static boolean isExcel2007(String fileName){
        if(fileName == null){
            fileName = "";
        }
        boolean isExcel2007 = fileName.toLowerCase().endsWith("xlsx") ? true : false;
        return isExcel2007;
    }

    /**
     *
     * @param file
     * @param cls
     * @param ifExNull,是否排除有空字段的数据.true:排除;false:不排除
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> Map<String, Object> parseExcelToObj(File file, Class<T> cls, boolean ifExNull) throws IllegalAccessException, InstantiationException {

        Workbook wb = null;
        try
        {
            if (isExcel2007(file.getName())) {
                wb = new XSSFWorkbook(new FileInputStream(file));
            } else {
                wb = new HSSFWorkbook(new FileInputStream(file));
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return null;
        }

        Map<String, Object> resultMap = new HashMap<String, Object>();

        List<T> list = new ArrayList<T>();//数据

        Sheet sheet = wb.getSheetAt(0);//获取第一张表

        Map<String, Integer> titlesIndexMap = getExcelTitles(sheet);
        int startRow = 0;
        if(titlesIndexMap.size() > 0){
            startRow = 1;
        }

        int lastRowNum = sheet.getLastRowNum();

        for (int i = startRow; i <= lastRowNum; i++)
        {
            T t = cls.newInstance();
            Field[] fields = t.getClass().getDeclaredFields();
            Row row = sheet.getRow(i);//获取索引为i的行,以0开始
            boolean flag = false;
            boolean isNull = false;

            for(Field field : fields){

                if (null!=field.getAnnotation(ColumnName.class)) {
                    setFieldValue(row, t, cls, field, titlesIndexMap);
                    flag = true;

                    //检查是否有值为null
                    field.setAccessible(true); // 设置属性是可以访问的(私有的也可以)
                    Object value = field.get(t);
                    if(value == null && ifExNull){
                        isNull = true;
                    }
                }

            }
            if(flag && !isNull){
                list.add(t);
            }
        }

        resultMap.put("title", titlesIndexMap.keySet());//excel的每列标题
        resultMap.put("data", list);// excel的数据

        return resultMap;
    }

    /**
     * 设置对象field值
     *
     * @param row
     * @param obj
     * @param cls
     * @param field
     * @param indexs
     */
    private static <T> void setFieldValue(Row row, T obj, Class<T> cls, Field field,
                           Map<String, Integer> indexs) {

        Map<String, Method> methods = getWriteMethods(cls);
        Map<String, Class<?>> types = getTypes(cls);
        String key = field.getName();
        Method m = methods.get(key);
        Class<?> type = types.get(key);
        String cm = field.getAnnotation(ColumnName.class).value();
        Integer index = indexs.get(cm);
        if (index != null) {
            Cell cell = row.getCell(index.intValue());
            if (cell != null) {
                setFieldStringValue(m, cell, type, obj);
                setFieldBooleanValue(m, cell, type, obj);
                setFieldDateValue(m, cell, type, obj);
                setFieldDoubleValue(m, cell, type, obj);
                setFieldFloatValue(m, cell, type, obj);
                setFieldIntegerValue(m, cell, type, obj);
                setFieldLongValue(m, cell, type, obj);
                setFieldBigDecimalValue(m, cell, type, obj);
            }
        }

    }

    /**
     * 设置对象field值(String型)
     *
     * @param m
     * @param cell
     * @param type
     * @param obj
     */
    private static <T> void setFieldStringValue(Method m, Cell cell, Class<?> type, T obj) {

        try {
            int ctype = cell.getCellTypeEnum().ordinal();
            String cellStyle = cell.getCellStyle().getDataFormatString();
            if (type == String.class && ctype == CellType.STRING.ordinal()) {
                m.invoke(obj, cell.getStringCellValue());
            } else if (type == String.class && ctype == CellType.NUMERIC.ordinal()) {
                if(cellStyle != null && cellStyle.indexOf("%") != -1){
                    // 百分比类型转换为带百分号的字符串
                    BigDecimal value = BigDecimal.valueOf(cell.getNumericCellValue());
                    value = value.multiply(new BigDecimal(100));
                    value = value.setScale(1, BigDecimal.ROUND_HALF_UP);
                    String finalValue = String.valueOf(value) + "%";
                    m.invoke(obj, finalValue);
                } else {
                    int decimalDigit = Utils.getNumberDecimalDigits(cell.getNumericCellValue());
                    if (decimalDigit == 1 || decimalDigit == 2){
                        // 一位小数或两位小数
                        m.invoke(obj, String.valueOf(cell.getNumericCellValue()));
                    } else {
                        long value = (long)cell.getNumericCellValue();
                        m.invoke(obj, String.valueOf(value));
                    }
                }
            } else if (type == String.class && ctype == CellType.FORMULA.ordinal()) {
                m.invoke(obj, cell.getCellFormula());
            }
        } catch (Exception e) {
        }
    }

    /**
     * 设置对象field值(Integer型)
     *
     * @param m
     * @param cell
     * @param type
     * @param obj
     */
    private static <T> void setFieldIntegerValue(Method m, Cell cell, Class<?> type, T obj) {
        try {
            if (type == Integer.class || type == int.class) {
                m.invoke(obj, (int) cell.getNumericCellValue());
            }
        } catch (Exception e) {
        }
    }

    /**
     * 设置对象field值(Long型)
     *
     * @param m
     * @param cell
     * @param type
     * @param obj
     */
    private static <T> void setFieldLongValue(Method m, Cell cell, Class<?> type, T obj) {
        try {

            if (type == Long.class || type == long.class) {
                m.invoke(obj, (long) cell.getNumericCellValue());
                return;
            }

        } catch (Exception e) {
        }
    }

    /**
     * 设置对象field值(Float型)
     *
     * @param m
     * @param cell
     * @param type
     * @param obj
     */
    private static <T> void setFieldFloatValue(Method m, Cell cell, Class<?> type, T obj) {
        try {

            if (type == Float.class || type == float.class) {
                m.invoke(obj, (float) cell.getNumericCellValue());
                return;
            }

        } catch (Exception e) {
        }
    }

    /**
     * 设置对象field值(Double型)
     *
     * @param m
     * @param cell
     * @param type
     * @param obj
     */
    private static <T> void setFieldDoubleValue(Method m, Cell cell, Class<?> type, T obj) {
        try {
            if (type == Double.class || type == double.class) {
                m.invoke(obj, cell.getNumericCellValue());
            }

        } catch (Exception e) {
        }
    }

    /**
     * 设置对象field值(BigDecimal型)
     *
     * @param m
     * @param cell
     * @param type
     * @param obj
     */
    private static <T> void setFieldBigDecimalValue(Method m, Cell cell, Class<?> type, T obj) {
        try {
            if (type == BigDecimal.class){
                m.invoke(obj, cell.getNumericCellValue());
            }

        } catch (Exception e) {
        }
    }

    /**
     * 设置对象field值(Boolean型)
     *
     * @param m
     * @param cell
     * @param type
     * @param obj
     */
    private static <T> void setFieldBooleanValue(Method m, Cell cell, Class<?> type, T obj) {
        try {

            if (type == Boolean.class || type == boolean.class) {
                m.invoke(obj, cell.getBooleanCellValue());
                return;
            }

        } catch (Exception e) {
        }
    }

    /**
     * 设置对象field值(Date型)
     *
     * @param m
     * @param cell
     * @param type
     * @param obj
     */
    private static <T> void setFieldDateValue(Method m, Cell cell, Class<?> type, T obj) {
        try {
            if (type == Date.class) {
                m.invoke(obj, cell.getDateCellValue());
                return;
            }
        } catch (Exception e) {
        }
    }

    private static <T> Map<String, Integer> getExcelTitles(Sheet sheet){

        Map<String, Integer> titlesIndexMap = new HashMap<String, Integer>();
        Row row = sheet.getRow(0);
        int count = row.getPhysicalNumberOfCells();

        for(int i = 0; i < count; i++){
            titlesIndexMap.put(row.getCell(i).getStringCellValue(), i);
        }

        return titlesIndexMap;
    }


}

备注:

1、合并单元格的方法sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, startCol, endCol));  CellRangeAddress各参数的含义分别是合并起始行、合并结束行、合并起始列、合并结束列。

2、poi提供的合并只是假合并,即仅将数据隐藏了,并没有进行数据的实际合并,这样在针对导出的合并数据进行计算时(如求和)就会将隐藏的数据一并计算。因此在检查到需要合并时,只保留一行数据,清楚合并的其他行数据,处理的代码片段:

// 合并单元格时,只保留一行数据,其它数据清空,否则数据求和时影响数据计算
int startRow = i-rowspan+2;//合并起始行
int endRow = i+1;//合并结束行
for(int m = startRow + 1; m <= endRow; m++){
    Row row = sheet.getRow(m);
    row.getCell(k).setCellValue("");//k表示列号
}

猜你喜欢

转载自blog.csdn.net/long2010110/article/details/87882286