excel导出百万数据与进度条展示

前言

需求:用户在UI界面上选择想要导出的列,然后点击导出按钮,就能导出用户想要的数据。

效果展示

excel导出百万数据与进度条展示_第1张图片

可能会产生的问题
1.如果导出数据量较大,接口很容易造成超时。
2.如果把数据一次性装载到内存里,很容易造成OOM与GC。
3.如果数据量太大sql语句查询也会很慢。

以下是批量导出解决的方案,如有更好的办法请指出。

1.在pom文件中导入poi的包

		<poi.version>4.1.2</poi.version>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>${poi.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>${poi.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml-schemas</artifactId>
			<version>${poi.version}</version>
		</dependency>

2.查询导出数据条数

select count(id) from you_table_name;

3.通过总条数计算需要进行几次分页查询
每次查询20w条数据,每次向excel输出1w条,减轻对内存的压力,减少用户等待时长。

 int pageSize = 200000;  // 每页显示的记录数
 if (totalCount > 200000) {
  int totalPages = (int) Math.ceil((double) totalCount / pageSize);
  for (int page = 1; page <= totalPages; page++) {
   int offset = (page - 1) * pageSize;
   //组装SQL语句
   Map<String, Object> sqlParamsMap = getDownloadOrgDataLimit(filters, filedList, offset, pageSize);
   String sql = (String) sqlParamsMap.get("sql");
   //查询数据
   List<Map<String, String>> dataset = listOfSQL(sql.toString());
   int totalSize = dataset.size();
   //定义每次只输出1万条
   int batchSize = 10000;
   for (int i = 0; i < totalSize; i += batchSize) {
       int endIndex = Math.min(i + batchSize, totalSize);
       //批次
       List batch = Arrays.asList(dataset.toArray()).subList(i, endIndex);
       Iterator<Map> it = batch.iterator();
        while (it.hasNext()) {
        	Row row = sheet.createRow(index);
        	//循环输出数据...
		}
    }
  }
 }

4.为了让UI界面更直观的观察到导出进度可以加一个进度条

 //导出的进度条信息
   double dPercent=(double)index/totalCount;   //将计算出来的数转换成double
   int  percent=(int)(dPercent*100);               //再乘上100取整
   request.getSession().setAttribute("curCount", index);
   request.getSession().setAttribute("percent", percent);    //比如这里是50
   request.getSession().setAttribute("percentText",percent+"%");//这里是50%
   //...也可以增加当前导出条数,剩余条数

5.前端需要频繁获取进度条进度

			//下载数据
 			downloadOrgData(){
 					//将刷新进度条状态打开
                    this.downloadFlag=true;
                    //执行进度条刷新方法
                    this.flushProgress();
                    axios.get("/org/orgDataDownload/",{
                        params:{
                            filedList: this.filedList.toString()
                        },
                        timeout:600000,
                        responseType:'blob'
                    }).then(res=>{
                        var blob = new Blob([res.data])
                        let fileName =  decodeURI(res.headers['content-disposition'].split("filename= ")[1])
                        var downloadElement = document.createElement('a');
                        var href = window.URL.createObjectURL(blob); //创建下载的链接
                        downloadElement.href = href;
                        downloadElement.download = fileName; //下载后文件名
                        document.body.appendChild(downloadElement);
                        downloadElement.click(); //点击下载                  
                        document.body.removeChild(downloadElement); //下载完成移除元素  
                        window.URL.revokeObjectURL(href); //释放掉blob对象
                        this.downloadFlag=false;    //关闭状态
                    })
                },
 			flushProgress(){
                    $.ajaxSettings.async = false;
                    //刷新进度条
                  if (this.downloadFlag){
                      window.setTimeout(function(){
                          var timer=window.setInterval(function(){
                              axios.get("/org/flushProgress").then(res=>{
                                  let progress =  res.data.data.map.percent;

                                  const progressBar = document.querySelector('.progress');
                                  const progressText = document.querySelector('.progress-text');
                                  if (progress===null){
                                      progressText.innerText = `数据准备中`;
                                  }else if (progress===100){
                                      progressText.innerText = `文档下载中`;
                                  } else {
                                      progressBar.style.width = `${progress}%`;
                                      progressText.innerText = `${progress}%`;
                                  }
                                  if(res.data.data.map.percent=="100"){
                                      window.clearInterval(timer);
                                  }
                              })
                          },800);
                      },800);
                  }
                    $.ajaxSettings.async = true;
                },

其他方案:如果请求超时时间太长,配置也不能修改的情况下,可以使用异步导出。
1.导出成功后将excel上传到OSS,用户直接下载OSS中文件。
2.导出成功后将消息发送到mq中,mq消费消息时通知用户下载excel.

你可能感兴趣的:(springboot,数据库,excel,大数据,java,spring,boot,vue.js,数据库)