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();
	}

到此爲止,介紹完畢,這些方法都是非常實用的乾貨,每個方法都有詳細的註釋,一目瞭然。

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