Jeecgboot 开发坑---大文件导出的问题

  • 遇到一个新需求, 关联12张表进行数据查询, 大概150个字段,然后导出xls。  大概的数据1W 左右。 于是想到了三种方法;
  1. JEECGBOOT 在线报表--------- 直接SQL 报错,放弃了
  2. 积木报表---------- 采用分页, 能显示数据,但是导出xls  10分钟都没有结果;也放弃了
  3. 模板导出---------- 最后一条路了,必须要能用呀。  结果 10M 一个问题,客户端等待导出还是不太理想
  • 必须要改造一下了。于是想到了任务模式,客户发起一下导出任务,然后等待后台生成好了再下载。

  1. 首先建立一个任务列表的增删改查
  2. 业务模块需要增加一个 button ,来实现业务逻辑
  3. 跳转到 任务列表,然后等待执行结束,点击下载按钮
    @RequestMapping(value = "/exportBigXls")
    public Result<?> exportBigXls(HttpServletRequest request, CommTask commTask) throws Exception {
    	//新建一条任务记录 获取组装需要的相关数据  模板地址 输出路径地址
    	
    	long time=System.currentTimeMillis();
    	
    	String templatepath="templates/temp.xlsx";
    	String filename=time+".xlsx";
    	String filepath="templates/";
    	
    	CommTask comm =new CommTask();
    	comm.setTaskid(time+"");
    	comm.setState("处理中...");
    	comm.setPath("");
    	commTaskService.save(comm);
    	log.debug("数据保存成功:"+comm.getId());
    	
    	
    	WriteFile file=new WriteFile(templatepath,filename,filepath,commTaskService,comm);
    	
    	file.start();
    	
    	
    	
    	
         return Result.OK("任务加载成功,前往任务列表进行数据处理!");
    }

也不是很复杂,生成文件,需要写一个新线程的类。

package org.jeecg.modules.demo.commtask.service;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.poi.ss.usermodel.Workbook;
import org.jeecg.modules.demo.commtask.entity.CommTask;
import org.jeecgframework.poi.excel.ExcelExportUtil;
import org.jeecgframework.poi.excel.entity.TemplateExportParams;

public class WriteFile extends Thread{  
	
	private String templatepath;
	private String filename;
	private String filepath;
	private ICommTaskService commTaskService;
	private CommTask commtask;
	
	//构造函数
	public WriteFile(String templatepath,String filename,String filepath,ICommTaskService commTaskService,CommTask comm) {
		this.templatepath=templatepath;
		this.filename=filename;
		this.filepath=filepath;
		this.commTaskService=commTaskService;
		this.commtask=comm;
	}
	
	
    @Override  
    public void run() {  
    	System.out.println("==========写文件线程开始启动!==========");  
    	
    	TemplateExportParams params =new TemplateExportParams(templatepath);
  	  Map<String, Object> map = new HashMap<String, Object>();
        map.put("title", "员工个人信息");
        map.put("name", "大熊");
        map.put("age", 22);
        map.put("company", "北京机器猫科技有限公司");
        map.put("date", "2020-07-13");
        Workbook workbook = ExcelExportUtil.exportExcel(params, map);
        File savefile = new File(filepath);
        if (!savefile.exists()) {
            savefile.mkdirs();
        }
        try {

        FileOutputStream fos = new FileOutputStream(filepath+filename);
        workbook.write(fos);
        fos.close();
        
        }catch(Exception ex) {

        	
        }
        
        try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        
        
        commtask.setEndTime(new Date());
        commtask.setPath(filepath+filename);
        commtask.setState("完成");
        commTaskService.updateById(commtask);

    }  
  
}  

具体的逻辑,根据自己的需要写就可以了。


记录一下前端vue的坑   定时任务setInterval 的用法。 前端页面需要刷新。 当然,非常好的办法是实现消息机制,这样实现最好。但是懒的很,先刷新页面吧。

 export default {
    name: 'CommTaskList',
    mixins:[JeecgListMixin, mixinDevice],
    components: {
      CommTaskModal
    },
    data () {
      return {
        description: '文件下载任务管理页面',
        // 表头
        columns: [
          {
            title: '#',
            dataIndex: '',
            key:'rowIndex',
            width:60,
            align:"center",
            customRender:function (t,r,index) {
              return parseInt(index)+1;
            }
          },
          {
            title:'任务编号',
            align:"center",
            dataIndex: 'taskid'
          },
          {
            title:'状态',
            align:"center",
            dataIndex: 'state'
          },
          {
            title:'下载路径',
            align:"center",
            dataIndex: 'path'
          },
          {
            title:'结束时间',
            align:"center",
            dataIndex: 'endTime'
          },
          {
            title:'备注',
            align:"center",
            dataIndex: 'remark'
          },
          {
            title: '操作',
            dataIndex: 'action',
            align:"center",
            fixed:"right",
            width:147,
            scopedSlots: { customRender: 'action' }
          }
        ],
        url: {
          list: "/commtask/commTask/list",
          delete: "/commtask/commTask/delete",
          deleteBatch: "/commtask/commTask/deleteBatch",
          exportXlsUrl: "/commtask/commTask/exportXls",
          importExcelUrl: "commtask/commTask/importExcel",
          myTask:"/commtask/commTask/exportBigXls"

        },
        dictOptions:{},
        superFieldList:[],
        timer: '',
      }
    },
    created() {
      this.getSuperFieldList();


      this.A();
    },
    computed: {
      importExcelUrl: function(){
        return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`;
      },
    },
    methods: {
      initDictConfig(){
      },
      myTask(){
        getAction(this.url.myTask).then((res) => {
          this.msg = res.result;
          if (res.success) {
            this.$message.success(this.msg);
          }else{
            this.$message.warning(this.msg);
          }
        })
      },
      down(record){
           var file=record.path;

           if(file==''){
             this.$message.warning("任务执行中");

           }else{
             //下载文件
             window.open(file);
           }
      },
      newData(){
        this.loadData();
      },
      getSuperFieldList(){
        let fieldList=[];
        fieldList.push({type:'string',value:'taskid',text:'任务编号',dictCode:''})
        fieldList.push({type:'string',value:'state',text:'状态',dictCode:''})
        fieldList.push({type:'string',value:'path',text:'下载路径',dictCode:''})
        fieldList.push({type:'datetime',value:'endTime',text:'结束时间'})
        fieldList.push({type:'string',value:'remark',text:'备注',dictCode:''})
        this.superFieldList = fieldList
      }

    },
    mounted() {
      this.timer = setInterval(this.newData, 5000);
    },
    beforeDestroy() {
      clearInterval(this.timer);
    }

  }

需要注销, 要不然一直执行。 vue 用的时间不长,第一次遇到。


最后的一点思考,如果 xls 文件非常大,可以继续分割文件,生成多个文件来实现。


版权声明:本文为qianlei0007原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。