基本上任何系统都需要导出功能, 例如导出公司的财务报表、 导出公司的全部员工信息等。
在此实现一个具有过滤指定字段的导出方法, 并对多表连接返回的复杂数据结构进行处理。
前端代码(每个系统前端采用框架可能略有差别, 但是本质是一样的, 以下用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();
}
}