利用JAVA及JS进行Excel文件导出处理方式

本文讲述了作者在旧项目中遇到的前端文件上传,后台处理并生成文件返回给前端的需求。通过拆分接口、使用Blob和原生Ajax实现,同时讨论了线程安全问题及改进方法。寻求更高效、一次请求解决的解决方案。

本人的一个老项目,前端用jquery写的,遇到一个需求,前端上传文件,后台接收到文件后,处理文件中数据并生成文件返回给前端导出,在开发过程中遇到了一些问题,采用曲线救国的方式实现了,记录一下,也希望哪位大神有更好的方式指点一下。
首先,单独写了不接受外部输入,直接一个生成文件返回给前端的测试接口:

后台代码:

    @GetMapping("testCalc")
    public void testExport(HttpServletResponse response) throws IOException {
        //导出数据
        OutputStream outputStream = null;
        try (HSSFWorkbook wb = calcFeeService.getTestWorkbook()) {
            //设置Http响应头告诉浏览器下载这个附件
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment;Filename=" + "TEST" + sdf.format(new Date()) + ".xls");
            outputStream = response.getOutputStream();
            wb.write(outputStream);
        } catch (Exception ex) {
            throw new ServiceException("导出Excel出现异常,异常信息:" + ex.getMessage());
        } finally {
            if (outputStream != null) {
                outputStream.flush();
                outputStream.close();
            }
        }
    }

开始的时候用jquery写前端,没有找到合适的方法。直接用window.open倒是可以,不过参数需要传入接口的全路径,不同环境域名不一样,每次发布都要修改路径。还有一个弊端,重新打开窗口,若遇到后台抛出业务异常,则无法给出友好的弹框提示,报错信息直接返回到了另一个新的标签页。后面参考网友的一篇文章(https://blog.youkuaiyun.com/zhazha0807/article/details/118734395),改用了原生ajax方式,设置responseType为blob,才成功,代码如下。

前端代码(直接用原生ajax发起请求):

function doTest() {
    download('calcFee/testCalc.do', 'test');
}
function download(url,filename) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    // xhr.setRequestHeader('token', 'xxxxxxx'); //用来做身份校验的字符串
    xhr.responseType = "blob"; // 设置返回类型blob
    // 定义请求完成的处理函数,请求前也可以增加 加载框/禁用下载按钮的相关逻辑
    xhr.onload = function() {
        if (this.status === 200) {
            var responseObj = this.response;
            var responseType = responseObj.type;
            var reader = new FileReader();
            reader.readAsDataURL(responseObj);
            reader.onload = function(e) {
                if(responseType === 'application/json'){//遇业务异常返回,前端弹框提示错误信息
                    var msg = e.target.result.replace('data:application/json;base64,', '');
                    layerAlert(JSON.parse(base64Decode(msg)).message,2,"警告");
                    doGetObjects();
                }else{
                    // 转换完成后创建a标签下载
                    var a = document.createElement('a');
                    a.download = filename;
                    a.href = e.target.result;
                    $("body").append(a);
                    a.click();
                    $(a).remove();
                }
            }
        }
    };
    xhr.send() //发送ajax请求
}

直接无参数传入后台发起文件导出请求没有问题。然而,业务需求是必须要有输入的,输入内容为excel文件流,无法直接追加到url路径后面,必须用post方式请求。而下载接口必须为get方式请求,无奈之下 ,只能将这个需求拆分成两个接口,一个接口为接收前端文件传入,处理传入的数据,将处理结果暂存。前端调用数据预处理接口成功后,发起文件导出请求。

后台接口1:(接收输入,处理数据)

    @PostMapping("doCalcFee")
    @ResponseBody
    public ResultVO calcFee(MultipartFile mFile) throws Exception{
        InputStream is = null;
        try{
            if(mFile != null){
                File tmpDir = new File(AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir")));
                if(!tmpDir.exists()){
                    tmpDir.mkdirs();
                }
                File tempFile = File.createTempFile("baseForm", ".xls");
                mFile.transferTo(tempFile);
                is = new FileInputStream(tempFile);
                tempFile.deleteOnExit();
            }else{
                throw new ServiceException("输入文件不能为空!");
            }
            calcFeeService.clearCacheData();
            calcFeeService.doBusiness(is);
        }catch (Exception e){
            throw new ServiceException("解析数据文件出现异常,异常信息:" + e.getMessage());
        }finally {
            if(is != null){
                is.close();
            }
        }
        return new ResultVO();
    }

后台接口2:(导出结果)

    @GetMapping("doExportResult")
    public void exportResult(HttpServletResponse response) throws Exception{
        if(calcFeeService.cacheIsEmpty()){
            throw new ServiceException("数据未准备好,请重新计算!");//全局异常处理类会将错误信息以json格式返回至前端
        }
        //导出数据
        OutputStream outputStream = null;
        try (HSSFWorkbook wb = calcFeeService.generateResultWorkbook()) {
            //设置Http响应头告诉浏览器下载这个附件
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment;Filename=" + sdf.format(new Date()) + ".xls");
            outputStream = response.getOutputStream();
            wb.write(outputStream);
        } catch (Exception ex) {
            throw new ServiceException("导出Excel出现异常,异常信息:" + ex.getMessage());
        } finally {
            if (outputStream != null) {
                outputStream.flush();
                outputStream.close();
            }
        }
    }

前端调用时先调用处理数据的post接口,成功后再调用下载结果文件的get接口

function doCalcFee() {
    var url = "calcFee/doCalcFee.do";
    $("#calcFormId").ajaxSubmit({
        type:'post',
        url:url,
        dataType:'json',
        beforeSend:function(){
            console.log('loader show')
            $("#load").show();
            $("#calcBtn").attr('disabled',true);//提交过程中,将计算按钮设置为不可点击
        },
        success:function(result){
            $("#load").hide();
            $("#calcBtn").attr('disabled',false);//接口返回成功后,将计算按钮恢复
            if(result.state==1){
                download('calcFee/doExportResult.do', '费用统计结果' + new Date().Format("yyyyMMddHHmmss"));
                doGetObjects();
            }else{
                layerAlert(result.message,2,"警告");
            }
        }
    });
}

完成!!!

部分内容参考 https://blog.youkuaiyun.com/zhazha0807/article/details/118734395

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值