需求.提供公共的可以按照一定条件查询出结果,并提供将查询结果全部下载功能(Excel、CSV、TXT),由于一次性查出结果放到内存会占用大量内存.需要支持分页模式查询出所有数据。
实现思路
1.在公共的controller(BaseController)中增加下载方法
2.支持自定义分页查询方式、自定义表头和查询结果属性对应
@ResponseBody @RequestMapping("/exportExcel.json") public void exportExcel(HttpServletRequest request, HttpServletResponse response, final DeductCurrentsQry qry) throws Exception { // 从零行开始导出 qry.setStart(0); // 分页设置大一点,提高导出效率 qry.setLimit(50); //开始导出数据 DownloadDataLoader<DeductCurrentVo> loader = new DownloadDataLoader<DeductCurrentVo>() { @Override protected List<DeductCurrentVo> getDownloadData(Integer pageNum) { // pageNum 初始值为0,在下载工具类中,通过对pageNum的自加,达到分页查询效果 qry.setStart(pageNum * qry.getLimit()); PageDataList<DeductCurrentVo> pageBean = getPageDataList(qry); return pageBean.getRows(); } }; String[] hearders = new String[] {"客户ID", "抵用券编号", "抵用券类型", "起投限制", "抵用券面值", "投资金额", "产品代码", "抵用券状态", "抵用券兑换码", "发放时间", "使用时间" }; String[] fields = new String[] { "customerId", "id", "deductType", "minInvestAmount", "faceValueFormat", "investAmount", "productCode", "deductStatus", "deductSn", "createDatetimeFormat", "usedDatetimeFormat" }; this.download(response,String.format("抵用券记录_%S.xls", DateUtil.dateStr(new Date())), Arrays.asList(hearders), null, loader, Arrays.asList(fields)); }
package com.wjs.common.web; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.httpclient.util.DateUtil; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.annotation.InitBinder; import com.wjs.common.util.StringEscapeEditor; import com.wjs.common.util.excel.ExcelUtils; /** * 基础控制器 * * 其他控制器继承此控制器获得日期字段类型转换和防止XSS攻击的功能 * * @author Moon * */ @Controller public class BaseController { private static final Logger LOGGER = LoggerFactory.getLogger(BaseController.class); @InitBinder public void initBinder(ServletRequestDataBinder binder) { /** * 自动转换日期类型的字段格式 */ // binder.registerCustomEditor(Date.class, new CustomDateEditor( // new SimpleDateFormat("yyyy-MM-dd"), true)); /** * 防止XSS攻击 */ binder.registerCustomEditor(String.class, new StringEscapeEditor(true, false)); } /** * http请求成功时调用 * * @return * * @author chenchunhui */ protected <T> JsonResult<T> success() { return this.success("操作成功", null); } /** * http请求成功时调用 * * @param data * 返回给前台的数据 * @return 返回给前台的标准json对象 */ protected <T> JsonResult<T> success(T data) { return this.success("操作成功", data); } /** * http请求成功时调用 * * @param msg * 信息说明 * @param data * 返回给前端的数据 * @param <T> * @return 返回给前台的标准json对象 * * @author chenchunhui */ protected <T> JsonResult<T> success(String msg, T data) { JsonResult<T> result = new JsonResult<T>(JsonResult.Status.SUCCESS, msg, data); if (LOGGER.isDebugEnabled()) { String logString = result.toString(); if (logString.length() > 1024) { logString = logString.substring(0, 1024); } LOGGER.debug(logString); } return result; } /** * http请求失败时调用 * * @return 返回给前台的标准json对象 * * @author chenchunhui */ protected <T> JsonResult<T> error() { return this.error("系统错误"); } /** * http请求失败时调用 * * @param msg * 信息说明 * @return 返回给前台的标准json对象 * * @author chenchunhui */ protected <T> JsonResult<T> error(String msg) { JsonResult<T> result = new JsonResult<T>(JsonResult.Status.ERROR, msg); if (LOGGER.isInfoEnabled()) { String logString = result.toString(); if (logString.length() > 1024) { logString = logString.substring(0, 1024); } LOGGER.info(logString); } return result; } /** * 分页下载数据获取类 * @author Silver * @date 2017年3月16日 上午11:45:13 * * @param <T> * */ protected abstract class DownloadDataLoader<T> { /** * 分页下载属性值控制操作类 * @param bean * @param propertyName * @param property * @return * @author Silver * @date 2017年3月16日 上午11:45:45 */ protected String convertProperty(T bean, String propertyName, Object property) { return property == null ? "" : property.toString(); } /** * 分页下载属性赋值 * @param bean * @param propertyName * @return * @author Silver * @date 2017年3月16日 上午11:46:37 */ protected Object getProperty(T bean, String propertyName) { try { return BeanUtils.getProperty(bean, propertyName); } catch (Throwable e) { LOGGER.info("bean:" + bean + ",Property:" + propertyName + e.getMessage(), e); return null; } } /** * 数据获取接口 * @param pageNum -- 从0计数 * @return * @throws Exception * @author Silver * @date 2017年3月16日 上午11:47:07 */ protected abstract List<T> getDownloadData(Integer pageNum) throws Exception; }; protected static interface Writer { public void write(Collection<String> row) throws IOException; } /** * Web下载文件 * @param response httpResponse信息 * @param fileName 文件名称,如果文件名称为空的情况默认【 日期.csv】格式 * @param header 表头名称 * @param columnWidth 列宽 * @param loader 数据加载类 * @param queryParam 如果有查询条件的传递 * @param propertyNames * @throws Exception * @author Silver * @date 2017年3月16日 上午11:47:31 */ protected <T> void download(HttpServletResponse response, String fileName, List<String> header, List<Integer> columnWidth, DownloadDataLoader<T> loader, List<String> propertyNames) throws Exception { if (StringUtils.isEmpty(fileName) || loader == null || CollectionUtils.isEmpty(propertyNames)) { throw new RuntimeException("参数错误。FileName:" + fileName + ",DataLoader:" + loader + ",PropertyName:" + propertyNames); } // 获取输出流,设置content-type等头域 final OutputStream out = getResponseStream(response ,fileName); try { Writer writer = null; // 获取文件后缀名 String extension = FilenameUtils.getExtension(fileName); // 如果是excel的后缀 if ("xls".equalsIgnoreCase(extension) || "xlsx".equalsIgnoreCase(extension)) { Workbook workbook = new HSSFWorkbook(); Sheet sheet = workbook.createSheet("sheet1"); final List<Collection<String>> rows = new ArrayList<Collection<String>>(); writer = new Writer() { @Override public void write(Collection<String> row) { rows.add(row); } }; writeOutputStream(loader, propertyNames, writer); // 写入excel if (!ExcelUtils.setExcelInfo(sheet, columnWidth, header, rows)) { throw new IOException("设置导出文件内容失败。"); } workbook.write(out); } else if("csv".equalsIgnoreCase(extension)) { writer = new Writer() { @Override public void write(Collection<String> row) throws IOException { String str = ExcelUtils.collectionToCsvString(row); byte[] content = org.apache.commons.codec.binary.StringUtils.getBytesUnchecked(str + "\n", "GBK"); IOUtils.write(content, out); out.flush(); } }; // 写文件头 writer.write(header); // 写文件 writeOutputStream(loader, propertyNames, writer); }else{ writer = new Writer() { @Override public void write(Collection<String> row) throws IOException { IOUtils.write(org.apache.commons.codec.binary.StringUtils.getBytesUnchecked(row + "\n", "GBK"), out); out.flush(); } }; // 写文件头 writer.write(header); // 写文件 writeOutputStream(loader, propertyNames, writer); } out.flush(); } finally { IOUtils.closeQuietly(out); } } /** * 获得输出流 * * @return * @throws IOException */ protected OutputStream getResponseStream(HttpServletResponse response, String fileName) throws IOException { if (StringUtils.isEmpty(fileName)) { fileName = DateUtil.formatDate(new Date(), "yyyy-MM-dd_HH-mm-ss") + ".csv"; } response.reset(); String extension = FilenameUtils.getExtension(fileName); if("xlsx".equalsIgnoreCase(extension)){ // 部分window版本生成后的xlsx打不开,默认改成xls打开 fileName = fileName.substring(0, fileName.length() -1); } //设置响应编码 response.setCharacterEncoding("UTF-8"); //设置对应的contentType response.setContentType("application/x-download;charset=UTF-8"); // response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes("gb2312"), "ISO-8859-1")); OutputStream out = response.getOutputStream(); return out; } protected <T> void writeOutputStream(DownloadDataLoader<T> loader, List<String> propertyNames, Writer writer) throws Exception { int pageNum = 0; int maxLenth = 102400; while (maxLenth-- > 0) { // 分页获取数据 List<T> objList = null; try { objList = loader.getDownloadData(pageNum++); } catch (Exception e) { LOGGER.error("获得到处数据异常:{}",e.getMessage(), e); } if (CollectionUtils.isEmpty(objList)) { break; } for (T bean : objList) { if (bean == null) { continue; } Collection<String> result = new ArrayList<String>(); // 遍历指定属性 for (String name : propertyNames) { // 获得属性值 Object property = loader.getProperty(bean, name); // 将属性值转换成字符串 String convertValue = loader.convertProperty(bean, name, property); // 组装成row result.add(convertValue); } if (CollectionUtils.isEmpty(result)) { continue; } writer.write(result); } } } }