欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前后端分离 AJAX 文件下载解决方案

程序员文章站 2024-03-20 16:30:58
...

【场景描述】

微服务架构中,使用前后端分离设计,用户点击导出下载Excel列表,通过AJAX与后端SpringMVC交互获取下载文件,请求需要携带Authorization认证信息,并且需要考虑到IE浏览器的兼容问题

【解决方案】

Ajax下载文件的这种方式本来就是禁止的。出于安全因素的考虑,javascript是不能够保存文件到本地的,

所以ajax考虑到了这点,只是接受json,text,html,xml格式的返回值,二进制的返回格式就会抛出这个异常。ajax请求只是个“字符型”的请求,即请求的内容是以文本类型存放的。

文件的下载是以二进制形式进行的,虽然可以读取到返回的response,但只是读取而已,是无法执行的,js是无法调用到浏览器的下载处理机制和程序。

如果非要使用ajax的话,只能通过返回值得到生成的文件相关url。一个对文件生成流的处理url,这样操作来实现文件下载且页面无刷新。

后端返回二进制文件,前端通过 blob 数据格式接收,并通过构建a链接URL的方式实现文件的下载

【前端代码】

1:注意考虑IE浏览器的XMLHttpRequest兼容性问题
2:设置响应接收的类型为blob
3: 请求类型这里设置为json,是因为我在传输FormData时后端一直获取失败于是使用这种方式获取参数
4:AJAX请求头中添加Authorization认证Token
5:创建Blob接收后端返回的文件流对象
6:通过返回值得到生成的文件相关url
7:IE浏览器使用link.click();没反应,需要使用window.navigator.msSaveBlob(blob, downloadFileName);构建

function excelExport(url, params) {
    let xhr;
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
    } else {
        //IE
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
    }
    xhr.open('post', url);
    xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8')
    xhr.responseType = 'blob';
    xhr.setRequestHeader("Authorization", "Bearer " + localStorage.getItem("access_token"))
    xhr.send(JSON.stringify(params));
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            const blob = new Blob([xhr.response])
            let urlBlob = window.URL.createObjectURL(blob)
            //创建一个a标签元素,设置下载属性,点击下载
            let link = document.createElement('a')
            link.href = urlBlob
            link.style.display = 'none'
            //取出下载文件名
            const fileName = xhr.getResponseHeader('fileName')
            const downloadFileName = decodeURIComponent(fileName)
            link.setAttribute('download', downloadFileName);
            //IE浏览器
            if (window.navigator.msSaveBlob) {
                try {
                    window.navigator.msSaveBlob(blob, downloadFileName);
                } catch (e) {
                    console.log(e);
                }
            } else {
                link.click();
            }
        }
    }
}

参考的前端调用示例

function ebotMonitorExport() {
    let exportUrl = "/api-m/XXXX/XXXX/export";
    let searchVo = {
        code: $("#code").val(),
        name: $("#name").val()
    };
    excelExport(exportUrl, searchVo);
}

【后端代码】

后端就是一般的导出逻辑,返回文件流,唯一注意的就是需要返回的文件名称放在响应流头中
setResponseHeader(response, fileName);

  @PostMapping("/export")
   public void export(@RequestBody ExportVO vo, HttpServletResponse response) {
        service.export(vo, response);
    }
 //根据条件查询数据
        List<XXX> list = mapper.selectByExample(vo);
		//构建Excel头
        String[] title = {"XXX", "XXX", "XXX"};
        String[] fieldNames = {"code", "name", "createTime"};
        String filename = "数据列表" + DateUtil.getNowDateStr() + ".xls";
        String sheetName = "数据列表";
        ExcelUtil.writeExcel(list, title, fieldNames, "createTime", sheetName, response, filename);
    public static void writeExcel(List list, String[] title, String[] fieldNames, String toDateField, String sheetName,
                                  HttpServletResponse response, String fileName) {
        //创建HSSFWorkbook
        HSSFWorkbook wb = getWorkbookDetail(list, title, fieldNames, toDateField, sheetName, null, null);
        try {
            fileName = URLEncoder.encode(fileName, "UTF8");
        } catch (UnsupportedEncodingException e) {
            log.error(LogHandle.error(String.format("出现异常: %s", e.getMessage())).toString());
        }
        setResponseHeader(response, fileName);
        try (OutputStream os = response.getOutputStream()) {
            wb.write(os);
        } catch (IOException e) {
            log.error(LogHandle.error(String.format("出现异常: %s", e.getMessage())).toString());
        }
    }
相关标签: 大前端 ajax