java使用Poi导出excel表格

随时随地阅读更多技术实战干货,获取项目源码、学习资料,请关注源代码社区公众号(ydmsq666)、博主微信(guyun297890152)、QQ技术交流群(183198395)。

在之前的一篇文章java操作Excel实战干货中展示了使用poi库读取excel表格的的用法,今天演示另一个常用功能,将数据导出到excel中,按照实战演练方式,后台提供接口,数据来源于数据库,然后导出excel输出到前端,详细如下:

接口代码:

	@RequestMapping(value = "/test/excel/export", method = { RequestMethod.POST })
	public ModelAndView exportExcel(@RequestBody RequestBean req) {
		// 查询数据
		List<Student> list = testService.getStudent(req);
		try {
			Map<String, Object> model = new HashMap<String, Object>();
			// 表格名
			model.put("sheetName", "学生表");
			// 文件名
			model.put("filename", "学生统计导出-" + new SimpleDateFormat("yyyyMMddhhmmss").format(new Date()) + ".xls");
			// 设置需要导出的数据
			model.put("items", list);
			// 设置需要显示的表头
			model.put("showName", new String[] { "学号", "姓名", "性别" });
			// 设置表头对应的属性
			model.put("fieldName", new String[] { "number", "name", "sex" });
			return new ModelAndView(new PoiExcelView(), model);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("导出excel出错,请检查;异常为:" + e.getMessage());
		}
	}

上面有详细注释,主要设置表格的一些属性,以及查询需要导出的实际内容,service简单介绍:

	public List<Student> getStudent(RequestBean req) {
		// 为了方便测试直接制造数据,实际中应该从数据库中读取
		List<Student> list = new ArrayList<Student>();
		list.add(new Student("001", "张三", "男"));
		list.add(new Student("002", "李四", "男"));
		list.add(new Student("003", "小娟", "女"));
		return list;
	}

这里为了方便测试,直接写死的数据。

测试用的实体对象Student就三个属性,这里就列代码了。

excel核心控件:PoiExcelView:

package com.home.javaee.view;

import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
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.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.web.servlet.view.document.AbstractXlsView;

import com.home.base.data.CollectionUtil;
import com.home.base.data.StringUtil;
import com.home.base.reflect.ReflectUtil;

/**
 * Excel控件
 * 
 * @author fengjian
 *
 */
public class PoiExcelView extends AbstractXlsView {

	private static Logger logger = Logger.getLogger(PoiExcelView.class);

	@Override
	protected void buildExcelDocument(Map<String, Object> model, Workbook arg1, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		OutputStream os = null;
		String sheetName = "";
		try {
			// 获取表格名
			sheetName = (String) model.get("sheetName");
			// 获取文件名
			String filename = (String) model.get("filename");
			// 获取显示表头
			String[] showName = (String[]) model.get("showName");
			// 获取表头对应属性
			String[] fieldName = (String[]) model.get("fieldName");
			// 获取列表数据
			List<?> list = (List<?>) model.get("items");
			response.setContentType("APPLICATION/OCTET-STREAM");
			String fileNameURL = URLEncoder.encode(filename, "UTF-8");
			// 设置响应header
			response.setHeader("Content-Disposition",
					"attachment; filename=" + fileNameURL + ";filename*=utf-8''" + fileNameURL);
			// 获取输入流
			os = response.getOutputStream();
			HSSFWorkbook workbook = (HSSFWorkbook) arg1;
			// 创建表格
			HSSFSheet sheet = workbook.createSheet(sheetName);
			// 设置表格总标题和每列的表头以及样式
			addTitle(sheet, showName, getHeaderStyle(workbook), getContentStyle(workbook));
			// 设置表格内容以及样式
			addContent(sheet, list, fieldName, getContentStyle(workbook), false, null);
			// 输出表格
			workbook.write(os);
		} catch (Throwable e) {
			logger.error(sheetName + "导出出错:" + e.getMessage(), e);
		} finally {
			try {
				os.flush();
				os.close();
			} catch (Throwable e) {
				logger.error(sheetName + "导出Excel出错:" + e.getMessage(), e);
			}
		}
	}

	/**
	 * 获取标题栏样式配置
	 * 
	 * @param workbook
	 * @return
	 */
	public static HSSFCellStyle getHeaderStyle(HSSFWorkbook workbook) {
		HSSFCellStyle format = workbook.createCellStyle();
		// 创建字体
		HSSFFont font = workbook.createFont();
		// 加粗
		font.setBold(true);
		// 黑体
		font.setFontName("黑体");
		// 字体大小
		font.setFontHeightInPoints((short) 16);
		// 垂直居中
		format.setVerticalAlignment(VerticalAlignment.CENTER);
		// 水平居中
		format.setAlignment(HorizontalAlignment.CENTER);
		// 设置字体
		format.setFont(font);
		return format;
	}

	/**
	 * 获取内容样式配置
	 * 
	 * @param workbook
	 * @return
	 */
	public static HSSFCellStyle getContentStyle(HSSFWorkbook workbook) {
		HSSFCellStyle format = workbook.createCellStyle();
		// 创建字体
		HSSFFont font = workbook.createFont();
		// 宋体
		font.setFontName("宋体");
		// 垂直居中
		format.setVerticalAlignment(VerticalAlignment.CENTER);
		// 水平居中
		format.setAlignment(HorizontalAlignment.CENTER);
		// 设置字体
		format.setFont(font);
		// 下边框样式
		format.setBorderBottom(BorderStyle.THIN);
		// 左边框样式
		format.setBorderLeft(BorderStyle.THIN);
		// 上边框样式
		format.setBorderTop(BorderStyle.THIN);
		// 右边框样式
		format.setBorderRight(BorderStyle.THIN);
		return format;
	}

	/**
	 * 给表格设置每列的标题,格式:表格第一行合并居中用表格名称作为整个表格的标题,第二行才作为列标题,第三行开始为实际内容
	 * 
	 * @param sheet
	 * @param titleNames
	 *            列标题数组
	 * @param headerStyle
	 *            表格标题样式
	 * @param contentStyle
	 *            内容样式
	 */
	public static void addTitle(HSSFSheet sheet, String[] titleNames, HSSFCellStyle headerStyle,
			HSSFCellStyle contentStyle) {
		// 合并单元格,把第一行按照数据的列宽进行合并,然后用表格名称作为标题,比如'员工工资表'
		sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, titleNames.length - 1));
		// 取出第一行
		HSSFRow row = sheet.createRow(0);
		// 第一格也就是合并后的整个单元格
		HSSFCell cell = row.createCell(0);
		// 用表格名作为主标题
		cell.setCellValue(sheet.getSheetName());
		// 设置标题样式
		cell.setCellStyle(headerStyle);

		// 用第二行作为每列数据的列标题
		row = sheet.createRow(1);
		// 循环填充列标题,并设置样式
		for (int i = 0; i < titleNames.length; i++) {
			cell = row.createCell(i);
			cell.setCellValue(titleNames[i]);
			cell.setCellStyle(contentStyle);
		}
	}

	/**
	 * 添加表格内容,从指定行开始
	 * 
	 * @param sheet
	 * @param list
	 *            列表数据
	 * @param fieldName
	 *            属性名数组
	 * @param contentStyle
	 *            内容样式
	 * @param isHaveSerial
	 *            是否添加序号
	 * @param startRow
	 *            起始行
	 */
	public static <T> void addContent(HSSFSheet sheet, List<T> list, String[] fieldName, HSSFCellStyle contentStyle,
			boolean isHaveSerial, Integer startRow) {
		try {
			if (CollectionUtil.isEmpty(list)) {
				return;
			}
			if (startRow == null) {
				startRow = 2;
			}
			HSSFRow row = null;
			HSSFCell cell = null;
			List<T> tList = (List<T>) list;
			T t = null;
			String value = "";
			// 填充列表内容
			for (int i = 0; i < list.size(); i++) {
				row = sheet.createRow(i + startRow);
				t = tList.get(i);
				for (int j = 0; j < fieldName.length; j++) {
					value = StringUtil.objectToString(getFieldValueByName(t, fieldName[j]));
					if (isHaveSerial) {
						// 首列加序号
						if (row.getCell(0) != null && row.getCell(0).getStringCellValue() != null) {
							cell = row.createCell(0);
							cell.setCellValue("" + i);
						}
						cell = row.createCell(j + 1);
					} else {
						cell = row.createCell(j);
					}
					cell.setCellValue(value);
					cell.setCellStyle(contentStyle);
				}
			}
			for (int j = 1; j < fieldName.length; j++) {
				sheet.autoSizeColumn(j); // 单元格宽度 以最大的为准
			}
		} catch (Throwable e) {
			logger.error("填充内容出现错误:" + e.getMessage(), e);
		}
	}

	/**
	 * 获取某个对象指定属性的值
	 * 
	 * @param obj
	 * @param fieldName
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static Object getFieldValueByName(Object obj, String fieldName) {
		try {
			if (StringUtil.isEmpty(fieldName) || obj == null) {
				return null;
			}
			Object fieldValue = null;
			// 如果是map直接根据key取值
			if (obj instanceof Map) {
				Map map = (Map) obj;
				fieldValue = map.get(fieldName);
			} else {
				// 如果是javabean对象,直接反射调用getter方法
				fieldValue = ReflectUtil.invokeGetterMethod(obj, fieldName);
			}
			return fieldValue;
		} catch (Throwable e) {
			logger.error("获取属性值出现异常:" + e.getMessage(), e);
			return null;
		}
	}

}

下面进行简要解析

buildExcelDocument是构造表格的核心回调方法,首先获取外部传过来的一些数据和属性,然后在用这些数据设置表头和列表内容,然后进行输出

getHeaderStyle是自定义的表格第一行名称的样式

getContentStyle是自定义表格内容列表包括表头的样式

addTitle是添加标题和表头,作为表格的第一第二行

addContent是添加表格的实际列表内容

上面是涉及到表格导出的一些核心方法,另外还涉及到一些辅助方法,简单介绍下:

getFieldValueByName作用是对数据库查出来的数据对象,根据其属性获取对应的值,比如根据name属性获取值'张三'

ReflectUtil的invokeGetterMethod是一个工具方法,反射调用某个属性的getter方法,相关方法如下:

	/**
	 * 反射执行某个属性的getter方法
	 * 
	 * @param obj
	 * @param fieldName
	 * @return
	 */
	public static Object invokeGetterMethod(Object obj, String fieldName) {
		return invokeMethod(obj, getGetterName(fieldName), null);
	}
	/**
	 * 根据getter方法的规则,构造某个属性的get方法名
	 * 
	 * @param fieldName
	 *            属性名
	 * @return
	 */
	public static String getGetterName(String fieldName) {
		// 属性的第一个字母转换为大写
		return "get" + fieldName.substring(0, 1) + fieldName.substring(1);
	}
/**
	 * 通过类对象,运行指定方法
	 * 
	 * @param obj
	 *            类对象
	 * @param methodName
	 *            方法名
	 * @param params
	 *            参数值
	 * @return 失败返回null
	 */
	public static Object invokeMethod(Object obj, String methodName, Object[] params) {
		if (obj == null || StringUtil.isEmpty(methodName)) {
			logger.error("参数异常,obj=" + obj + ",methodName=" + methodName);
			return null;
		}

		Class<?> clazz = obj.getClass();
		try {
			Class<?>[] paramTypes = null;
			if (params != null) {
				paramTypes = new Class[params.length];
				for (int i = 0; i < params.length; ++i) {
					paramTypes[i] = params[i].getClass();
				}
			}
			Method method = clazz.getMethod(methodName, paramTypes);
			method.setAccessible(true);
			return method.invoke(obj, params);
		} catch (NoSuchMethodException e) {
			logger.error("method " + methodName + " not found in " + obj.getClass().getName());
		} catch (Exception e) {
			logger.error("Exception:" + e);
		}
		return null;
	}

StringUtil的objectToString用来将其他数据类型统一成字符串,便于显示:

	/**
	 * 将对象转换为字符串
	 * 
	 * @param object
	 * @return
	 */
	public static String objectToString(Object object) {
		if (object == null) {
			return "";
		}
		if (object instanceof Date) {
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			return df.format((Date) object);
		}
		if (object instanceof String) {
			return (String) object;
		}
		if (object instanceof Integer) {
			return ((Integer) object).intValue() + "";
		}
		if (object instanceof Double) {
			return ((Double) object).doubleValue() + "";
		}
		if (object instanceof Long) {
			return Long.toString(((Long) object).longValue());
		}
		if (object instanceof Float) {
			return Float.toHexString(((Float) object).floatValue());
		}
		if (object instanceof Boolean) {
			return Boolean.toString((Boolean) object);
		}
		if (object instanceof Short) {
			return Short.toString((Short) object);
		}
		return object.toString();
	}

到此为止,介绍完毕,这些方法都是非常实用的干货,每个方法都有详细的注释,一目了然。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章