【分享】如何实现异步导出Excel?

今天分享JBolt极速开发平台中使用的异步下载文件的解决方案。

需求场景:

列表查询界面有一个【导出Excel】按钮,需要点击按钮,弹出loading信息框,异步调用后台action,得到数据库数据后生成Excel数据然后响应发送到前端,异步请求拿到数据后,当做文件下载下来。

重点:

异步请求,导出Excel,前端拿到后做成文件下载


解决方案:

1、前端异步请求

点击按钮的时候,执行一个异步请求,去访问Action

<button type="button" onclick="ajaxDownload('admin/user/exportExcel')">导出Excel</button>
//异步下载
function ajaxDownload(url,fileName){

    var xhr = new XMLHttpRequest();
    xhr.timeout=jboltAjaxTimeout;
    xhr.open('GET', url, true); 
    xhr.responseType = "blob";    // 返回类型blob
    //定义请求完成的处理函数
    xhr.onload = function () {
        // 请求完成
        if (this.status === 200) {
            // 返回200
            var blob = this.response;
            var reader = new FileReader();
            reader.readAsDataURL(blob);    // 转换为base64,可以直接放入a href
            reader.onload = function (e) {
                // 转换完成,创建一个a标签用于下载
                var a = document.createElement('a');
                if(fileName){
                    a.download =fileName;
                }else{
                    var headerName=xhr.getResponseHeader("Content-disposition");
                    if(headerName){
                        headerName=decodeURI(headerName);
                        var dl_filenameArr=headerName.split("=");
                        if(dl_filenameArr&&dl_filenameArr.length==2){
                            a.download =$.trim(dl_filenameArr[1]);
                        }
                    }else{
                        alert("下载失败,未设置下载文件的filename");
                        return false;
                    }
                }
                a.href = e.target.result;
                jboltBody.append(a);    // 修复firefox中无法触发click
                a.click();
                $(a).remove();
            }
        }else{
           alert("网络异常");
        }
    };
    // 发送ajax请求
    xhr.send();
}


好,这样前端在点击按钮的时候就会执行异步请求,去请求指定的URL地址。

那么后端就需要处理这个请求,从数据库查询后做成excel。


2、后端写出Excel文件流

注意:为了方便演示 看清楚后端导出流程 我没有再service里写 直接在action中快速写完逻辑。

大体意思就是读取数据,把生成的Excel文件流形式写出给Response。

其中header中的content-disposition属性值里的filename是自定义的。

前端按钮上调用ajaxDownload如果传入一个名字的话,会按照传入的名字去下载生成文件。具体是前端工作了。

/**
 * 导出Excel
 */
public void exportExcel() {
	HttpServletResponse response=getResponse();
	response.setHeader("Content-Disposition", "attachment;filename=jbolt.xls");
	response.setHeader("Pragma", "no-cache");
	response.setHeader("Cache-Control", "no-cache");
	response.setHeader("Accept-Ranges", "bytes");
	response.setDateHeader("Expires", 0);
	response.setContentType("application/vnd.ms-excel");
	//使用hutool 导出excel
	ExcelWriter excelWriter=ExcelUtil.getWriter();
	List<User> users=service.findAll();
	int row=0;
	//循环遍历
	for(User user:users) {
		excelWriter.writeCellValue(0,row, user.getId());
		excelWriter.writeCellValue(1,row, user.getName());
		excelWriter.writeCellValue(2,row, user.getPinyin());
		row++;
	}
	//导出下载文件流
	try {
		excelWriter.flush(response.getOutputStream());
	} catch (IORuntimeException | IOException e) {
		e.printStackTrace();
	}
	excelWriter.close();
}


看一下效果:

image.png

image.png

这个按钮的高级延伸版,我在JBolt极速开发平台里实现的,后端做了更完善的处理,大体原理就是这样。

http://jbolt.cn/jbolt.html

可以登录演示版,找到这个异步按钮,点击感受一下!


image.png


有问题就加我微信

微信:mumengmeng

image.png


评论区

haojay

2020-04-26 22:50

最好做一个数据导出的任务页面,统一到这个页面下载。提交导出后,返回任务正在处理,请稍后前往数据导出任务页面下载。

akak

2020-04-27 09:21

我这边的处理是大数据下载新启线程生成excel文件,完成后通过websocket通知用户下载。

山东小木

2020-04-27 16:53

@akak 这个应用场景不一样的 比如在阿里云 rds导出数据的时候 就是你说的这种 异步下载 通过任务 导出 我这个虽然是异步导出 但是需要点了 立马给我导出来

穿越123

2020-04-28 10:59

@山东小木 你说的异步只是前端的异步,而不是后台异步对吧

山东小木

2020-04-28 11:28

@穿越123 前端异步请求

IvyHelen

2020-04-29 15:53

excel 2003 挺占用内存的,然后excel 2007 提供了SXSSFWorkbook 可以试一下。
104w之后,再分sheet2存储

山东小木

2020-04-29 17:39

@IvyHelen 恩 JBolt里支持BigExcel导出

风满楼

2020-05-22 16:51

下载开始到完成有个loading,文件下载完成loading继而停止,那这个状态怎么回传到前台啊?

山东小木

2020-05-22 20:16

@风满楼 用的类似ajax的通讯啊 有success回调