ResultHandler流式查询导出Excel数据
// 导出Excel头部名称 List<String> headerArray = Arrays.asList("编码","姓名"); // 数据库对应字段 List<String> fieldArray = Arrays.asList("id", "name"); // 定义Excel文件名,不带“xlsx”后缀。 String fileName = getToday()+"导出数据-共计("+count+")"+"条"; //每次导出New 一个 handler 对象。将headerArray,fieldArray,fileName 传入参数。 ExcelResultHandler<查询对象> handler = new ExcelResultHandler<查询对象>(headerArray,fieldArray,fileName) { public void tryFetchDataAndWriteToExcel() { // this 指的是 New 对象,可添加查询 条件参数。 // mapper的方法需要是void返回,并且参数中含ResultHandler(流式查询遍历的条件),可以加条件参数, // fetchSize参数必填。 //见下图 //<select id="export" parameterType="" resultType="" fetchSize="-2147483648"> ***Mapper.export(this); } }; //开始调用方法。原理:调用开始后,表头写入Excel,遍历结果集,一条一条数据写入Excel,最后关闭资源。 handler.startExportExcel();
源码会判断方法返回值是否void,是否含有ResultHandler
ExcelResultHandler工具类:
按需修改
public abstract class ExcelResultHandler<T> implements ResultHandler<T>{ private final Logger logger = LoggerFactory.getLogger(this.getClass()); private AtomicInteger currentRowNumber = new AtomicInteger(0); private Sheet sheet = null; private List<String> headerArray ; private List<String> fieldArray ; private int totalCellNumber; private boolean isExportZip = true; private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private String exportFileName = UUID.randomUUID().toString().replace("-", ""); public ExcelResultHandler(List<String> headerArray,List<String> fieldArray){ this.headerArray = headerArray; this.fieldArray = fieldArray; this.totalCellNumber = headerArray.size(); } public ExcelResultHandler(List<String> headerArray,List<String> fieldArray,boolean isExportZip){ this(headerArray,fieldArray); this.isExportZip = isExportZip; } public ExcelResultHandler(List<String> headerArray,List<String> fieldArray,String exportFileName){ this(headerArray,fieldArray); this.exportFileName = exportFileName; } public ExcelResultHandler(List<String> headerArray,List<String> fieldArray,String exportFileName,boolean isExportZip){ this(headerArray,fieldArray,exportFileName); this.isExportZip = isExportZip; } public abstract void tryFetchDataAndWriteToExcel(); public void handleResult(ResultContext<? extends T> resultContext) { Object aRowData = resultContext.getResultObject(); callBackWriteRowdataToExcel(aRowData); } public void startExportExcel() { HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); ZipOutputStream zos = null; OutputStream os = null; try { logger.info("--------->>>>写入Excel开始.." ); //写入文件 response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment;filename=" + new String((exportFileName+".zip").replaceAll(" ", "").getBytes("utf-8"),"iso8859-1")); os = new BufferedOutputStream(response.getOutputStream()); if(isExportZip){ zos = new ZipOutputStream(os); ZipEntry zipEntry = new ZipEntry(new String((exportFileName+".xlsx").replaceAll(" ", ""))); zos.putNextEntry(zipEntry); } SXSSFWorkbook wb = new SXSSFWorkbook(); wb.setCompressTempFiles(false); sheet = wb.createSheet("Sheet 1"); Row row = sheet.createRow(0); for (int cellNumber = 0; cellNumber < totalCellNumber; cellNumber++) { Cell cell = row.createCell(cellNumber); cell.setCellValue(headerArray.get(cellNumber)); } tryFetchDataAndWriteToExcel(); logger.info("--------->>>> write to excel size now is {}", currentRowNumber.get() ); if(isExportZip){ wb.write(zos); }else{ wb.write(os); } if (wb != null) { wb.dispose();// 删除临时文件,很重要,否则磁盘可能会被写满 } wb.close(); logger.info("--------->>>>全部数据写入Excel完成.." ); } catch (Exception e) { logger.error("系统异常,请联系管理员",e); } finally { logger.info("--------->>>>关闭流系统.." ); if(isExportZip){ try {if(zos!=null) zos.close();} catch (IOException e1) {logger.error("关闭流系统异常,请联系管理员",e1); } }else{ try {if(os!=null) os.close();} catch (IOException e1) {logger.error("关闭流系统异常,请联系管理员",e1); } } } } @SuppressWarnings("rawtypes") public void callBackWriteRowdataToExcel(Object aRowData) { MethodAccessor methodAccessor = Reflector.getMethodAccessor(aRowData.getClass()); currentRowNumber.incrementAndGet(); Row row = sheet.createRow(currentRowNumber.get()); for (int cellNumber = 0; cellNumber < totalCellNumber; cellNumber++) { Object value = null; if(aRowData instanceof Map){ value = ((Map)aRowData).get(fieldArray.get(cellNumber)); }else { value = methodAccessor.getFieldValue(aRowData, fieldArray.get(cellNumber)); } Cell cell = row.createCell(cellNumber); if (value!=null && value instanceof Date){ cell.setCellValue(sdf.format(value));// }else { cell.setCellValue(value==null?"":value.toString());//写入数据 } } if(currentRowNumber.get() % 5000 == 0 ){ logger.info("--------->>>> write to excel size now is {}", currentRowNumber.get() ); } } }
因为无返回值,前端如何知道是否下载完成?
前端用下载监听器。
var index = layer.load(3, { shade: [0.1, '#fff'], content: '<span class="loadtip">下载中</span>', success: function (layer) { layer.find('.layui-layer-content').css({ 'padding-top': '30px', 'width': '100px', }); layer.find('.loadtip').css({ 'font-size':'18px', 'margin-left':'5px' }); } }); const xhr = new XMLHttpRequest(); xhr.open('GET', '下载接口'); xhr.send(null); // 设置服务端的响应类型 xhr.responseType = "blob"; // 监听下载 xhr.addEventListener('progress', event => { // 计算出百分比 const percent = ((event.loaded / event.total) * 100).toFixed(2); console.log(`下载进度:${percent}`); }, false); xhr.onreadystatechange = event => { if (xhr.readyState == 4) { if (xhr.status == 200) { // 获取ContentType const contentType = xhr.getResponseHeader('Content-Type'); // 文件名称 如果想和后端一样,需要处理编码 const fileName = "名称.zip"; // 创建一个a标签用于下载 const donwLoadLink = document.createElement('a'); donwLoadLink.download = fileName; donwLoadLink.href = URL.createObjectURL(xhr.response); // 触发下载事件,IO到磁盘 donwLoadLink.click(); // 释放内存中的资源 URL.revokeObjectURL(donwLoadLink.href); // 关闭加载动画 layer.close(index); } else if (response.status == 404) { alert(`文件:${file} 不存在`); } else if (response.status == 500) { alert('系统异常'); } } }