需求.提供公共的能夠按照必定條件查詢出結果,並提供將查詢結果所有下載功能(Excel、CSV、TXT),因爲一次性查出結果放到內存會佔用大量內存.須要支持分頁模式查詢出全部數據。前端
實現思路java
1.在公共的controller(BaseController)中增長下載方法web
2.支持自定義分頁查詢方式、自定義表頭和查詢結果屬性對應spring
@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); } } } }
package com.wjs.common.util.excel; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanMap; import org.apache.log4j.Logger; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.hssf.usermodel.HSSFRichTextString; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.util.CollectionUtils; /** * * @author liqiang05 * */ public class ExcelUtils { /** * logger */ private static final Logger logger = Logger.getLogger(ExcelUtils.class); /** * 設置列格式 * * @param sheet * @param columnWidthList */ public static boolean setSheetStyle(Sheet sheet, List<Integer> columnWidthList) { if (sheet == null || columnWidthList == null) { return false; } // 設置全部列的寬度 for (int indx = 0, iMax = columnWidthList.size(); indx < iMax; indx++) { Integer columnWidth = columnWidthList.get(indx); if (columnWidth == null) { continue; } sheet.setColumnWidth(indx, columnWidth.intValue() * 256); } return true; } /** * 設置行信息 * * @param row * @param rowObj * @return */ public static boolean setRowInfo(Row row, Collection<Object> rowObj) { if (row == null || rowObj == null) { if (logger.isInfoEnabled()) { logger.info("Row:" + row + ",rowObj" + rowObj); } return false; } // 填充每一列數據 int indxColumn = 0; for (Object object : rowObj) { Cell cell = row.createCell(indxColumn++); if (object == null) { if (logger.isDebugEnabled()) { logger.debug("Row:" + row + ",Column:" + indxColumn + ",is empty"); } continue; } String columnValue = object.toString(); cell.setCellType(HSSFCell.CELL_TYPE_STRING); cell.setCellValue(columnValue); } return true; } /** * 設置行信息 * * @param row * @param rowObj * @param convert */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static boolean setRowInfo(Row row, Object rowObj, IExcelConvert convert) { if (row == null || rowObj == null) { if (logger.isInfoEnabled()) { logger.info("Row:" + row + ",rowObj" + rowObj); } return false; } try { Collection<Object> rowContent = null; if (convert != null) { rowContent = convert.convert(rowObj); } else if (rowObj instanceof Map) { rowContent = ((Map) rowObj).values(); } else if (rowObj instanceof Collection) { rowContent = (Collection) rowObj; } else { rowContent = (new BeanMap(rowObj)).values(); } if (rowContent == null || rowContent.isEmpty()) { if (logger.isDebugEnabled()) { logger.debug("Row:" + row + ",is empty"); } return false; } return setRowInfo(row, rowContent); } catch (Throwable e) { logger.info(rowObj + "convertFailed,row:" + row, e); return false; } } /** * 將數據寫入excel * * @param sheet * @param columnWidth * @param header * @param content * @return */ public static boolean setExcelInfo(Sheet sheet, List<Integer> columnWidth, List<String> header, List<?> rows) { return setExcelInfo(sheet, columnWidth, header, rows, null); } /** * 將數據寫入excel * * @param sheet * @param columnWidth * @param header * @param content * @param converter * @return */ public static boolean setExcelInfo(Sheet sheet, List<Integer> columnWidth, List<String> header, List<?> content, IExcelConvert converter) { if (sheet == null) { logger.info("sheet is null"); return false; } // 設置sheet格式 setSheetStyle(sheet, columnWidth); // 設置頭信息 int indxRow = 0; Row row = sheet.createRow(indxRow++); setRowInfo(row, header, null); // 若是內容爲空 則退出 if (content == null || content.isEmpty()) { logger.info("content is null,cannot write excel"); return true; } for (Object rowContent : content) { row = sheet.createRow(indxRow++); setRowInfo(row, rowContent, converter); } return true; } /** * 導出到excel * * @param title * sheet Title * @param columnWidthList * 全部列的寬度,能夠不指定 * @param content * 內容, 每一項為一行,每一行內是List表明全部列 * @return */ public static Workbook setupXls(String title, List<Integer> columnWidthList, List<List<String>> content) { Workbook wb = new HSSFWorkbook(); Sheet sheet = wb.createSheet(title); if (columnWidthList != null) { // 設置全部列的寬度 for (int indx = 0, iMax = columnWidthList.size(); indx < iMax; indx++) { Integer columnWidth = columnWidthList.get(indx); if (columnWidth == null) { continue; } sheet.setColumnWidth(indx, columnWidth.intValue() * 256); } } if (content == null || content.isEmpty()) { if (logger.isInfoEnabled()) { logger.info("content is null,cannot write excel,title:" + title); } return wb; } // 遍歷一行 for (int indxRow = 0, iMaxRow = content.size(); indxRow < iMaxRow; indxRow++) { Row row = sheet.createRow(indxRow); List<String> rowContent = content.get(indxRow); if (rowContent == null || rowContent.isEmpty()) { if (logger.isDebugEnabled()) { logger.debug("Row:" + indxRow + ",is empty,title:" + title); } continue; } // 填充每一列數據 for (int indxColumn = 0, iMaxColumn = rowContent.size(); indxColumn < iMaxColumn; indxColumn++) { Cell cell = row.createCell(indxColumn); String columnValue = rowContent.get(indxColumn); if (columnValue == null || columnValue.length() == 0) { if (logger.isDebugEnabled()) { logger.debug("Row:" + indxRow + ",Column:" + indxColumn + ",is empty,title:" + title); } continue; } cell.setCellValue(columnValue); } } return wb; } /** * 加載Excel 默認實現方式 * * @param wb * @return */ public static List<List<String>> loadXls(Workbook wb) { // 默認 只讀第一個sheet, 且從第二行開始遍歷,默認讀取到最大列 return loadXls(wb, 0, 1, 0); } /** * 加載excel * * @param wb * @param sheetIndx * 要加載excel的sheet頁的index * @param startRowIndx * 要加載Row的index * @param iMaxColumn * 最大讀到Cloumn的index * @return List<List<>> */ public static List<List<String>> loadXls(Workbook wb, int sheetIndx, int startRowIndx, int iMaxColumn) { List<List<String>> resList = new ArrayList<List<String>>(); if (wb == null || sheetIndx < 0 || startRowIndx < 0 || iMaxColumn < 0) { logger.error("param error,return empty list,Workbook:" + wb + ",sheetIndex:" + sheetIndx + ",startRowNo:" + startRowIndx + ",iMaxColumn:" + iMaxColumn); return resList; } Sheet sheet = wb.getSheetAt(sheetIndx); if (sheet == null) { logger.error("sheet is null,return empty list,Workbook:" + wb + ",sheetIndex:" + sheetIndx + ",startRowNo:" + startRowIndx); return resList; } // 從指定行開始遍歷 for (int indxRow = startRowIndx, iMaxRow = sheet.getLastRowNum(); indxRow <= iMaxRow; indxRow++) { Row row = sheet.getRow(indxRow); if (row == null) { if (logger.isDebugEnabled()) { logger.debug("Row is null,sheetIndex:" + sheetIndx + ",RowNo:" + indxRow); } continue; } List<String> rowContent = new ArrayList<String>(); // 當最大列為0時 讀取最大CellNum if (iMaxColumn == 0) { iMaxColumn = row.getLastCellNum(); } boolean hasContent = false; for (int indxColumn = 0; indxColumn < iMaxColumn; indxColumn++) { String cellValue = null; Cell cell = row.getCell(indxColumn); if (cell == null) { if (logger.isDebugEnabled()) { logger.debug("Cell is null,sheetIndex:" + sheetIndx + ",RowNo:" + indxRow + ",CellNo:" + indxColumn); } } else { cellValue = getCellStrValue(cell); } // 若是 讀到的內容不是空 表明這行有數據 if (cellValue != null && cellValue.length() > 0) { hasContent = true; } // 不論當前格是否有數據都加入. rowContent.add(cellValue); } // 這一行有內容 則加入 if (hasContent) { resList.add(rowContent); } } return resList; } public static String getCellStrValue(Cell cell) { String res = ""; try { res = cell.getStringCellValue(); } catch (Exception e) { DecimalFormat df = new DecimalFormat("#"); res = df.format(cell.getNumericCellValue()) + ""; } return res; } /** * * @Description: 將集合轉換成字符串輸出 * @param coll * @return 設定文件 * @throws 異常說明 * @author albert.su suzy@malam.com * @date 2014年5月7日 下午12:35:55 */ public static String collectionToCsvString(Collection<?> coll) { if (CollectionUtils.isEmpty(coll)) { return ""; } StringBuilder sb = new StringBuilder(); Iterator<?> it = coll.iterator(); while (it.hasNext()) { Object object = it.next(); if (String.valueOf(object).matches("[0-9,\\.]+") || String.valueOf(object).contains(",")) { sb.append("\"\t"); sb.append(object); sb.append("\""); } else { sb.append("\t"); sb.append(object); } if (it.hasNext()) { sb.append(","); } } return sb.toString(); } // 如下爲糯米的代碼, 建議少用 /** * 從InputStream讀取Excel workbook * * @param ins * @return * @throws IOException * @throws FileNotFoundException */ public static HSSFWorkbook readWorkbook(InputStream ins) throws IOException, FileNotFoundException { ByteArrayOutputStream byteOS = new ByteArrayOutputStream(); BufferedInputStream bis = new BufferedInputStream(ins); byte[] by = new byte[512]; int t = bis.read(by, 0, by.length); while (t > 0) { byteOS.write(by, 0, 512); // read 512 t = bis.read(by, 0, by.length); } byteOS.close(); InputStream byteIS = new ByteArrayInputStream(byteOS.toByteArray()); HSSFWorkbook wbDest = new HSSFWorkbook(byteIS); return wbDest; } public static void writeToResponse(HttpServletResponse response, HSSFWorkbook wb, String fileName) throws IOException { response.setContentType("application/ms-download"); response.setCharacterEncoding("gb2312"); response.setHeader("Content-Disposition", "filename=" + fileName); OutputStream out = response.getOutputStream(); wb.write(out); out.flush(); out.close(); } /** * 判斷單元格的格式 * * @param cell * 單元格 * @return String 將excel各類單元格的類型轉換爲String類型 */ public static String getCellStringValue(HSSFCell cell) { // 轉換後單元格的值 String value = ""; if (cell != null) { switch (cell.getCellType()) { case HSSFCell.CELL_TYPE_STRING: value = cell.getRichStringCellValue().getString(); break; case HSSFCell.CELL_TYPE_NUMERIC: if (HSSFDateUtil.isCellDateFormatted(cell)) { Date date = HSSFDateUtil.getJavaDate(cell.getNumericCellValue()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); value = sdf.format(date); } else { DecimalFormat formatter = new DecimalFormat("########"); value = formatter.format(cell.getNumericCellValue()); } break; case HSSFCell.CELL_TYPE_FORMULA: cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC); value = String.valueOf(cell.getNumericCellValue()); break; case HSSFCell.CELL_TYPE_BLANK: break; case HSSFCell.CELL_TYPE_BOOLEAN: break; case HSSFCell.CELL_TYPE_ERROR: break; default: break; } } return value; } /** * 到出excel的Helper類 * * @author Administrator * */ public static class ExcelRowBuilder { private HSSFRow row; private short index = 0; public ExcelRowBuilder(HSSFRow row) { this.row = row; } @SuppressWarnings("deprecation") public ExcelRowBuilder addCell(String str) { HSSFCell cell = row.createCell(index++); cell.setCellValue(new HSSFRichTextString(str)); return this; } @SuppressWarnings("deprecation") public ExcelRowBuilder addCell(long value) { HSSFCell cell = row.createCell(index++); cell.setCellValue(value); return this; } @SuppressWarnings("deprecation") public ExcelRowBuilder addCell(double value) { HSSFCell cell = row.createCell(index++); cell.setCellValue(value); return this; } } }