扩展excel导出工具

web工程中需要大量的excel导出功能,但是每个导出功能都直接调用POI原生代码,会导致代码量的冗余,大量和业务无关的代码和业务代码混杂在一起,稀释了业务代码,降低代码的可读性。同时导出excel通用的部分很多,单独书写也降低了代码的复用性。

于是我就从网上找到了一个excel导出工具类NewExportToExcelUtil.java,可以支持单个sheet的excel导出,只要传入对象数组即可。但是我在使用过程中,很多需求不能够满足,于是进行了四项扩展,使得他更加强大。

1.兼容Map:

数组内容之前只能从从Object的属性和值的解析,但是我们很多项目数组里存的是Map,于是我扩展了对Map键值对的解析,用Map的键相当于object的属性,Map的值相当于object的值,于是实现了和前面方法的无缝对接,从而实现了对Map支持的扩展。

方法就是在解析之前,对Object的类进行判断,如果是Map就当Map解析,否则执行之前的解析方法。代码如下:

2.从单sheet到多sheet:

之前传参的数据是数组对象,扩展为多sheet,需要把数组再组合成一个更大的数组。于是传参格式之上又封装了一层数组。原来生成单个sheet的代码之外包装了一层循环,便实现了多sheet的支持。代码如下:

3.合并列的支持:

有些需求里,需要把同列相同的元素合并起来,有的不需要,我就使用一个布尔值的参数来控制合并的开关。当合并开关打开时,我们在生成完sheet之后再进行合并处理,为了找出相同内容的列,我们先把sheet里的数据存入一个比对数组。然后循环遍历比对数组,对符合要求的列进行poi的列合并操作,代码如下:

4.增加隐藏列的支持:

有些需要excel导入的表格里需要存储一些ID信息作为列的唯一标示,但是这些列需要隐藏起来,以免用户手动修改破坏了这些值。我没有通过增加参数来实现,因为参数太多增加了工具的复杂性。于是我们对列的名称下想办法,当列的名称以hide_开头时,该列隐藏。虽然hide_开头的表头不好看,但是隐藏起来就看不到了,这个缺点是发现不了的,可以忽略掉。(新特性3:sheet是否需要合并时我也想到过这个方案,但是sheet的值用户是可以看到的,加上特定的格式会在文件里被看出来,于是选择了扩展参数的方案)当生成完sheet时进行列隐藏操作,遍历所有的列表,当列名符合hide_开头的条件时,执行POI的列隐藏操作。代码如下:

 

增加这四个特性后,excel导出需要的大多数需求基本上都能通过这四个特性实现了。

源代码如下:

package com.ibaby.framework.utils;

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletResponse;

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.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.lang.System.out;


/**
 * 导出Excel
 * @author Guang
 *
 */
public class NewExportToExcelUtil {

    private static final Logger logger =LoggerFactory.getLogger(NewExportToExcelUtil.class);

    /**
     * 导出Excel
     * @param excelName   要导出的excel名称
     * @param list   要导出的数据集合
     * @param fieldMap 中英文字段对应Map,即要导出的excel表头
     * @param response  使用response可以导出到浏览器
     * @return
     */
    public static <T> void export(String excelName,String[] sheets,List<T>[] list,LinkedHashMap<String, String>[] fieldMap,boolean[] merges,HttpServletResponse response){

        // 设置默认文件名为当前时间:年月日时分秒
        if (excelName==null || excelName=="") {
            excelName = new SimpleDateFormat("yyyyMMddhhmmss").format(
                    new Date()).toString();
        }
        // 设置response头信息
        response.reset();
        response.setContentType("application/vnd.ms-excel"); // 改成输出excel文件
        try {
            response.setHeader("Content-disposition", "attachment; filename="
                    +new String(excelName.getBytes("gb2312"), "ISO-8859-1")  + ".xls");
        } catch (UnsupportedEncodingException e1) {
            logger.info(e1.getMessage());
        }

        try {
            //创建一个WorkBook,对应一个Excel文件
            HSSFWorkbook wb=new HSSFWorkbook();
            //创建单元格,并设置值表头 设置表头居中
            HSSFCellStyle style=wb.createCellStyle();
            //创建一个居中格式
            style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
            style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
            style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
            HSSFFont font = wb.createFont(); 
            font.setFontName("宋体");
            font.setFontHeightInPoints((short) 12);// 设置字体大小
            style.setFont(font);
			// 指定当单元格内容显示不下时自动换行
            style.setWrapText(true);
            
            for(int i=0;i<sheets.length;i++){
	            //在Workbook中,创建一个sheet,对应Excel中的工作薄(sheet)
	            HSSFSheet sheet=wb.createSheet(sheets[i]);
	            // 填充工作表
	            fillSheet(sheet,list[i],fieldMap[i],style,merges[i]);
	            //隐藏列
	            int index = 0; //列数
	            for (Entry<String, String> entry : fieldMap[i].entrySet()) {
	                String value = entry.getValue();
	                if(value != null && value.indexOf("hide_")==0){
	                	sheet.setColumnHidden(index,true);
	                }
	                index++;
	            }
            }

            //将文件输出
            OutputStream ouputStream = response.getOutputStream();
            wb.write(ouputStream);
            ouputStream.flush();
            ouputStream.close();
        } catch (Exception e) {
            logger.info("导出Excel失败!");
            logger.error(e.getMessage());
        }
    }
    /**
     * 导出Excel到本地
     * @param excelName   要导出的excel名称
     * @param list   要导出的数据集合
     * @param fieldMap 中英文字段对应Map,即要导出的excel表头
     * @param response  使用response可以导出到浏览器
     * @param response  path  本地路径
     * @return
     */
    public static <T> void exportTopath(String excelName,String[] sheets,List<T>[] list,LinkedHashMap<String, String>[] fieldMap,boolean[] merges,HttpServletResponse response,String realPath){

        // 设置默认文件名为当前时间:年月日时分秒
        if (excelName==null || excelName=="") {
            excelName = new SimpleDateFormat("yyyyMMddhhmmss").format(
                    new Date()).toString();
        }
        // 设置response头信息
/*        response.reset();
        response.setContentType("application/vnd.ms-excel"); // 改成输出excel文件*/
/*        try {
            response.setHeader("Content-disposition", "attachment; filename="
                    +new String(excelName.getBytes("gb2312"), "ISO-8859-1")  + ".xls");
        } catch (UnsupportedEncodingException e1) {
            logger.info(e1.getMessage());
        }*/

        try {
            //创建一个WorkBook,对应一个Excel文件
            HSSFWorkbook wb=new HSSFWorkbook();
            //创建单元格,并设置值表头 设置表头居中
            HSSFCellStyle style=wb.createCellStyle();
            //创建一个居中格式
            style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
            style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
            style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
            HSSFFont font = wb.createFont();
            font.setFontName("宋体");
            font.setFontHeightInPoints((short) 12);// 设置字体大小
            style.setFont(font);
			// 指定当单元格内容显示不下时自动换行
            style.setWrapText(true);

            for(int i=0;i<sheets.length;i++){
	            //在Workbook中,创建一个sheet,对应Excel中的工作薄(sheet)
	            HSSFSheet sheet=wb.createSheet(sheets[i]);
	            // 填充工作表
	            fillSheet(sheet,list[i],fieldMap[i],style,merges[i]);
	            //隐藏列
	            int index = 0; //列数
	            for (Entry<String, String> entry : fieldMap[i].entrySet()) {
	                String value = entry.getValue();
	                if(value != null && value.indexOf("hide_")==0){
	                	sheet.setColumnHidden(index,true);
	                }
	                index++;
	            }
            }

            //将文件输出
/*            OutputStream ouputStream = response.getOutputStream();
            wb.write(ouputStream);*/
            // 填写有误
            FileOutputStream out = null;
            out = new FileOutputStream(realPath);
            wb.write(out);

            out.flush();
            out.close();
        } catch (Exception e) {
            logger.info("导出Excel失败!");
            logger.error(e.getMessage());
        }
    }

    /**
     * Remove a row by its index
     * @param sheet a Excel sheet
     * @param rowIndex a 0 based index of removing row
     */
    public static void removeRow(Sheet sheet, int rowIndex) {
        int lastRowNum=sheet.getLastRowNum();
        if(rowIndex>=0&&rowIndex<lastRowNum)
            sheet.shiftRows(rowIndex+1,lastRowNum,-1);//将行号为rowIndex+1一直到行号为lastRowNum的单元格全部上移一行,以便删除rowIndex行
        if(rowIndex==lastRowNum){
            Row removingRow=sheet.getRow(rowIndex);
            if(removingRow!=null)
                sheet.removeRow(removingRow);
        }
    }

    /**
     * 根据字段名获取字段对象
     *
     * @param fieldName
     *            字段名
     * @param clazz
     *            包含该字段的类
     * @return 字段
     */
    public 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 fieldName  字段名
     * @param o          对象
     * @return           字段值
     * @throws Exception 异常
     *          
     */
    public static Object getFieldValueByName(String fieldName, Object o)
            throws Exception {

        Object value = null;
        if("class java.util.HashMap".equals(o.getClass().toString())){
        	return ((Map)o).get(fieldName);
        }
        //根据字段名得到字段对象
        Field field = getFieldByName(fieldName, o.getClass());

        //如果该字段存在,则取出该字段的值
        if (field != null) {
            field.setAccessible(true);//类中的成员变量为private,在类外边使用属性值,故必须进行此操作
            value = field.get(o);//获取当前对象中当前Field的value
        } else {
            throw new Exception(o.getClass().getSimpleName() + "类不存在字段名 "
                    + fieldName);
        }

        return value;
    }

    /**
     * 根据带路径或不带路径的属性名获取属性值,即接受简单属性名,
     * 如userName等,又接受带路径的属性名,如Baby.department.name等
     *
     * @param fieldNameSequence 带路径的属性名或简单属性名
     * @param o                 对象
     * @return                  属性值
     * @throws Exception        异常
     *             
     */
    public 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 {
            // 根据数组中第一个连接属性名获取连接属性对象,如Baby.department.name
            Object fieldObj = getFieldValueByName(attributes[0], o);
            //截取除第一个属性名之后的路径
            String subFieldNameSequence = fieldNameSequence
                    .substring(fieldNameSequence.indexOf(".") + 1);
            //递归得到最终的属性对象的值
            value = getFieldValueByNameSequence(subFieldNameSequence, fieldObj);
        }
        return value;

    }

    /**
     * 向工作表中填充数据
     *
     * @param sheet
     *            excel的工作表名称
     * @param list
     *            数据源
     * @param fieldMap
     *            中英文字段对应关系的Map
     * @param style
     *            表格中的格式
     * @throws Exception
     *             异常
     *            
     */
    public static <T> void fillSheet(HSSFSheet sheet, List<T> list,
            LinkedHashMap<String, String> fieldMap,HSSFCellStyle style,boolean merge) throws Exception {
        // 定义存放英文字段名和中文字段名的数组
        String[] enFields = new String[fieldMap.size()];
        String[] cnFields = new String[fieldMap.size()];

        // 填充数组
        int count = 0;
        for (Entry<String, String> entry : fieldMap.entrySet()) {
            enFields[count] = entry.getKey();
            cnFields[count] = entry.getValue();
            count++;
        }

        //在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制short
        HSSFRow row=sheet.createRow((int)0);

        // 填充表头
        for (int i = 0; i < cnFields.length; i++) {
            HSSFCell cell=row.createCell(i);
            cell.setCellValue(cnFields[i]);
            cell.setCellStyle(style);
            sheet.setColumnWidth(i, 6000);
            //sheet.autoSizeColumn(i);
        }

        //生成对比数组
        String[][] compares = new String[list.size()][enFields.length];
        // 填充内容
        for (int index = 0; index < list.size(); index++) {
            row = sheet.createRow(index + 1);
            // 获取单个对象
            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();

                HSSFCell cell=row.createCell(i);
        		cell.setCellValue(fieldValue);
        		cell.setCellStyle(style);
        		//写入对比数组
        		for(int j=i;j<enFields.length;j++){
        			if(compares[index][j]==null){
        				compares[index][j] = fieldValue;
        			}else{
        				compares[index][j] += ";"+fieldValue;
        			}
        		}
            }
        }
        //检查合并
        if(merge){
        	for (int j = 0; j < enFields.length; j++){
	        	int from = 0,to=0;
	        	for(int i=0;i<list.size(); i++) {
	        		if(compares[i][j].equals(compares[from][j])){
	        			to = i;
	        		}else{
	        			if(from!=to){
		        			CellRangeAddress cellRangeAddress = new CellRangeAddress(from+1, to+1, j, j);
		                    sheet.addMergedRegion(cellRangeAddress);
	        			}
	        			from=to=i;
	        		}
	        		if(i+1==list.size() && from!=to){
	        			CellRangeAddress cellRangeAddress = new CellRangeAddress(from+1, to+1, j, j);
	                    sheet.addMergedRegion(cellRangeAddress);
	        		}
	        	}
	        }
        }
    }

    public static String ShiftNull(Object obj){
    	if(obj==null)
    		return "";
    	return obj.toString();
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值