基本上任何系统都需要导出功能, 例如导出公司的财务报表、 导出公司的全部员工信息等。
在此实现一个具有过滤指定字段的导出方法, 并对多表连接返回的复杂数据结构进行处理。
前端代码(每个系统前端采用框架可能略有差别, 但是本质是一样的, 以下用jQuery EasyUI为例):主要功能就是获取前端表头数据, 然后把数据格式转换成适当格式发送到服务器端。
/** * @param dataGridId 导出数据的表格ID * @param exportUrl 导出数据请求的URL * @param excelTitle excel文件名 * @param checkFlag 导出数据的表格是否有复选框(有复选框后台自动去掉该列) 0--无 1--有 * @param reduceColnumName 不用导出的列名 */ function exportExcelBaseInfo(dataGridId,exportUrl,excelTitle,checkFlag,reduceColnumName){ var $dg = $("#"+dataGridId+""); var params=$dg.datagrid('options').queryParams; // 参数 var columns=$dg.datagrid('options').columns; // 表头 var v_pageNumber = $dg.datagrid('options').pageNumber;//当前页号 var v_pageSize = $dg.datagrid('options').pageSize;//每页多少行记录 // [Object, Object, ...] Object里面多余属性去除,就留下有用的字段, 节省带宽 var columnsNew = []; $.each(columns,function(index,item){ var dataArray = []; $.each(item,function(rowIndex,rowData){ var v_object = {}; v_object.field = rowData.field; v_object.title = rowData.title; dataArray[rowIndex] = v_object; }); columnsNew[index] = dataArray; }); // 对象的类型转换成Json类型 //[Object, Object, ...] -----> "[[{"field":"name","title":"名称"},{"field":"gender","title":"性别"}, ... ]]" var exportColumns=JSON.stringify(columnsNew); var url=BasePath+exportUrl; // 行数据 var dataRow=$dg.datagrid('getRows'); $("#exportExcelForm").remove(); // 使用form表单提交前端表头及参数 $("<form id='exportExcelForm' method='post'></form>").appendTo("body"); var fromObj=$('#exportExcelForm'); if(dataRow.length>0){ fromObj.form('submit', { url: url, onSubmit: function(param){ param.exportColumns=exportColumns; param.fileName=excelTitle; param.checkFlag=checkFlag; param.reduceColnumName=reduceColnumName; param.pageNumber = v_pageNumber; param.pageSize = v_pageSize; if(params!=null&¶ms!={}){ $.each(params,function(i){ param[i]=params[i]; }); } }, success: function(){ } }); }else{ alert('记录为空,不能导出!',1); } }
导出核心类: 放在MVC架构的C层, 以下代码以SpringMVC为例放在Controller层。因为每种系统业务不同, 在此不提供Service层和DAO层等代码。
// 导出 @RequestMapping(value = "/do_export",method=RequestMethod.POST) public void doExportMemberlist(HttpServletRequest req, Model model, HttpServletResponse response) throws ManagerException { Map<String, Object> params = new HashMap<String, Object>(); String exportColumns = req.getParameter("exportColumns"); String fileName = req.getParameter("fileName"); String checkFlag = req.getParameter("checkFlag"); //增加参数,该参数可以不指定,使用默认值 String rowAccessWindowSizeStr = req.getParameter("rowAccessWindowSize"); params.put("exportColumns", "exportColumns"); params.put("fileName", "fileName"); params.put("checkFlag", "checkFlag"); params.put("rowAccessWindowSizeStr", "rowAccessWindowSizeStr"); String reduceColnumName = StringUtils.isEmpty(req.getParameter("reduceColnumName")) ? "" : req .getParameter("reduceColnumName"); if (!StringUtils.isNotEmpty(checkFlag)) { checkFlag = "0"; } ObjectMapper mapper = new ObjectMapper(); // 前端数据格式[[{"field":"name","title":"名称"},{"field":"gender","title":"性别"}, ... ]] // 转换为标准JSON格式 if (StringUtils.isNotEmpty(exportColumns)) { try { exportColumns = exportColumns.replace("[", ""); exportColumns = exportColumns.replace("]", ""); exportColumns = "[" + exportColumns + "]"; //Excel表头处理 List<Map> columnsList = mapper.readValue(exportColumns, new TypeReference<List<Map>>() { }); if (columnsList != null && columnsList.size() > 0) { // 有复选框字段则删除 if (StringUtils.isNotEmpty(checkFlag) && checkFlag.equals("1")) { columnsList.remove(0); } // 删除不在Excel输出的行 if (StringUtils.isNotEmpty(reduceColnumName)) { for (int i = 0; i < columnsList.size(); i++) { if (columnsList.get(i) != null) { if (columnsList.get(i).get("field") != null && reduceColnumName.equals(columnsList.get(i).get("field"))) { columnsList.remove(i); break; } } } } } List<ModelType> list = new ArrayList<ModelType>(); // 输出到Excel的总行数 int total = this.serviceMethod.findAllTriggerCount(params); SimplePage page = new SimplePage(1, total, (int) total); // 所有要输出的数据 list = this.serviceMethod.findAllTriggerList(page, "", "", params); List<Map> listArrayList = new ArrayList<Map>(); if (list != null && list.size() > 0) { for (ModelType vo : list) { Map map = new HashMap(); ExportUtil.object2MapWithoutNull(vo, map); // 在此可以对数据库内容进行转换成实际值 // int sex = (Integer)map.get("gender"); // if (sex == 0) { // map.put("sex", "男"); // }else{ // map.put("sex", "女"); // } listArrayList.add(map); } Integer rowAccessWindowSize = 1; if (rowAccessWindowSizeStr != null) rowAccessWindowSize = Integer.valueOf(rowAccessWindowSizeStr); HSSFExport.commonExportData(StringUtils.isNotEmpty(fileName) ? fileName : "导出信息", columnsList, listArrayList, response, rowAccessWindowSize); } } catch (Exception e) { // 异常处理 } } }
工具类:
ExportUtil.java
public class ExportUtil { // 将Object内容存入Map public static void object2MapWithoutNull(Object obj, Map<String,Object> map) throws IllegalArgumentException, IllegalAccessException { // 获得类的所有申明的字段,即包括public、private和proteced,但是不包括父类的字段 // 而getFields()获得某个类的所有的公共(public)的字段,包括父类 Field[] fields = obj.getClass().getDeclaredFields(); for (int j = 0; j < fields.length; j++) { fields[j].setAccessible(true); // 获取该属性值 if(fields[j].get(obj) != null){ // 进行数据处理, 处理后放入Map中 if((fields[j].get(obj) instanceof Date)){ SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); map.put(fields[j].getName(), sdf.format(fields[j].get(obj))); // 复杂类型如List<Object>递归转换 // instanceof Object }else if((fields[j].get(obj) instanceof ModelType)){ object2MapWithoutNull(fields[j].get(obj),map); }else{ map.put(fields[j].getName(), fields[j].get(obj)); } }else { map.put(fields[j].getName(),""); } } // 父类数据转换 Field[] fields2 = obj.getClass().getSuperclass().getDeclaredFields(); for (int j = 0; j < fields2.length; j++) { fields2[j].setAccessible(true); if(fields2[j].get(obj) != null){ if((fields2[j].get(obj) instanceof Date)){ SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); map.put(fields2[j].getName(), sdf.format(fields2[j].get(obj))); }else{ map.put(fields2[j].getName(), fields2[j].get(obj)); } }else { map.put(fields2[j].getName(),""); } } } }
生成Excel表类:HSSFExport.java
import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.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.HSSFFooter; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Footer; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.streaming.SXSSFWorkbook; /** * 导出查询数据到Excel表中(HSSF的开源框架) */ public final class HSSFExport { private HSSFExport() { } /** * 导出数据到Excel, 获取jQuery EasyUI的表头信息与查询条件, 暂时不支持合并的表头, 纵横转换的表头是单独写的 * @param fileName 文件名 * @param ColumnsMapList 表头数据 * @param dataMapList 正文数据 * @param response * @param rowAccessWindowSize 导出excel过程中,如果需要访问导的第几行数据,则,需要给定这个参数为访问的excel行数;如传递的为空,则默认值为1行, * 推荐使用默认值。 例如:如果想在程序中取得最后100行的数据,那么该参数=100; 否则就按照默认值导出。 * @throws Exception */ public static void commonExportData(String fileName, List<Map> ColumnsMapList, List<Map> dataMapList, HttpServletResponse response, Integer rowAccessWindowSize) throws Exception { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); String fileName2 = new String(fileName.getBytes("gb2312"), "iso-8859-1"); //文件名 response.setHeader("Content-Disposition", "attachment;filename=" + fileName2 + ".xlsx"); response.setHeader("Pragma", "no-cache"); if (rowAccessWindowSize == null) { rowAccessWindowSize = 1; } // 创建工作簿 SXSSFWorkbook wb = new SXSSFWorkbook(rowAccessWindowSize.intValue()); // 创建工作表 Sheet sheet1 = wb.createSheet(); wb.setSheetName(0, fileName); sheet1.setDefaultRowHeightInPoints(20); sheet1.setDefaultColumnWidth((short) 18); //设置页脚 Footer footer = sheet1.getFooter(); footer.setRight("Page " + HSSFFooter.page() + " of " + HSSFFooter.numPages()); //设置样式 表头--第一行 CellStyle style1 = wb.createCellStyle(); style1.setAlignment(HSSFCellStyle.ALIGN_CENTER); Font font1 = wb.createFont(); font1.setFontHeightInPoints((short) 13); font1.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); style1.setFont(font1); //设置样式 表头--第二行 CellStyle style2 = wb.createCellStyle(); style2.setAlignment(HSSFCellStyle.ALIGN_LEFT); style2.setWrapText(true); //合并 CellRangeAddress rg1 = new CellRangeAddress(0, (short) 0, 0, (short) (ColumnsMapList.size() - 1)); sheet1.addMergedRegion(rg1); //设置样式 表头--第三行 CellStyle style3 = wb.createCellStyle(); style3.setAlignment(HSSFCellStyle.ALIGN_LEFT); Font font3 = wb.createFont(); font3.setFontHeightInPoints((short) 18); font3.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); style3.setFont(font3); style3.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); style3.setFillPattern(CellStyle.SOLID_FOREGROUND); Row row0 = sheet1.createRow(0); row0.setHeightInPoints(35); //第一行 标题 Cell cell0 = row0.createCell((short) 0); cell0.setCellValue(fileName.toString()); cell0.setCellStyle(style3); //第二行 表头数据 Row row1 = sheet1.createRow(1); row1.setHeightInPoints(20); for (int i = 0; i < ColumnsMapList.size(); i++) { Cell cell1 = row1.createCell(i); cell1.setCellType(HSSFCell.ENCODING_UTF_16); cell1.setCellValue(ColumnsMapList.get(i).get("title").toString()); cell1.setCellStyle(style1); } //第三行 填充数据 for (int j = 0; j < dataMapList.size(); j++) { Row row2 = sheet1.createRow((j + 2)); // 第三行开始填充数据 Map cellDataMap = dataMapList.get(j); for (int i = 0; i < ColumnsMapList.size(); i++) { Cell cell = row2.createCell(i); String cellValue = StringUtils.EMPTY; if (ColumnsMapList.get(i).get("field") != null) { String fieldString = String.valueOf(ColumnsMapList.get(i).get("field")); cellValue = String.valueOf(cellDataMap.get(fieldString)); } cell.setCellValue(cellValue); cell.setCellStyle(style2); } } // 将以上缓存内容写到Excel中 wb.write(response.getOutputStream()); response.getOutputStream().flush(); response.getOutputStream().close(); wb.dispose(); } }