• Ajax方式文件下载(后台返回为json或文件流)


    在应用开发中,经常需要下载文件(如导出Excel),调用后台接口时,如果后台报错需要弹出错误信息,如果没有报错正常下载文件。本文主要介绍前台及后台(基于java)的处理方法,文中使用到的软件版本:Spring 4.3.9、Java 1.8.0_191、Jquery 1.12.4、Chrome 85.0.4183.121。

    1、实现思路

    方法一:通过原生Ajax响应头来区分是文本还是流,然后通过blob来处理返回数据

    方法二:分成两个接口,先调用生成文件的接口,如果文件成功生成则再调用文件下载的接口;如果文件生成失败则弹出失败信息

    2、方法一(Ajax响应头区分文本还是流)

    2.1、后台

    /**
     * 根据sql导成csv;成功返回文件流,失败返回错误信息
     * @param sql 导出sql
     * @param col 字段信息,以逗号分隔
     * @param title 文件头信息,以逗号分隔
     * @param separator csv文件的分隔符
     * @param request
     * @param response
     */
    @RequestMapping("exportForCsv.do")
    @ResponseBody
    public CallResult<String> exportForCsv(String sql, String col, String title, String separator, String fileName, HttpServletRequest request, HttpServletResponse response) {
        logger.info("sql={}", sql);
        logger.info("title={}", title);
        logger.info("separator={}", separator);
        logger.info("fileName={}", fileName);
        logger.info("baseServiceName={}", baseServiceName);
        CallResult<String> result = null;
        ByteArrayOutputStream baos = null;
        InputStream in = null;
        try {
            if (Util.isNull(separator)) {
                separator = ",";
            }
            if (Util.isNull(fileName)) {
                fileName = "data.csv";
            }
            baos = new ByteArrayOutputStream();
            getService().exportForCsv(baos, sql, col, title, separator);//这边调用service把文件写入输入流,请自行实现
            in = new ByteArrayInputStream(baos.toByteArray());
            writeStream(in, fileName, request, response);
        } catch(Exception e) {
            result = new CallResult<>(-1, "发生异常");
            e.printStackTrace();
        } finally {
            FileUtil.close(baos);
            FileUtil.close(in);
        }
        return result;
    }
    private void writeStream(InputStream in, String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {
        OutputStream out = null;
        try {
            String urlFileName = "";
            if (request.getHeader("User-Agent").toLowerCase().indexOf("firefox") > 0) {
                urlFileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
            } else {
                urlFileName = java.net.URLEncoder.encode(fileName, "UTF-8");
            }
    
            response.reset();
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename="" + urlFileName + """);
            response.setHeader("Connection", "close");
    
            out = new BufferedOutputStream(response.getOutputStream(), BUFFER_SIZE);
            byte buf[] = new byte[BUFFER_SIZE];
            int len;
            while ((len = in.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
        } finally {
            FileUtil.close(out);
        }
    }

    2.2、前台

    2.2.1、拼接参数

    function expForCsv() {
        let sql = "";//请自行设置
        let col = "";//请自行设置
        let title = "";//请自行设置
    let fileName = "";//请自行设置
       let xhr = new XMLHttpRequest(); xhr.open("post", "${pageContext.request.contextPath}/common/export/exportForCsv.do", true); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.responseType = "blob"; xhr.onload = function() { if (xhr.getResponseHeader("Content-type") == 'application/octet-stream') { let url = window.URL.createObjectURL(xhr.response); let a = document.createElement("a"); a.href = url; a.style.display = 'none' a.download = fileName; a.click(); window.URL.revokeObjectURL(url); a.remove(); } else { let reader = new FileReader(); reader.addEventListener('loadend', function (e) { let data = JSON.parse(e.target.result); alert('提示', "系统出错,错误信息为:" + data.description + ",请将该信息提供给代维人员寻求帮助"); }); reader.readAsText(xhr.response); } }; xhr.send('sql=' + sql + '&col=' + col + '&title=' + title + '&separator=|&t=' + new Date().getTime()); }

    2.2.2、使用FormData

    function expForCsv(type) {
    let sql = "";//请自行设置 let col
    = "";//请自行设置 let title = "";//请自行设置 let fileName = ";//请自行设置 let xhr = new XMLHttpRequest(); xhr.open("post", "${pageContext.request.contextPath}/common/export/exportForCsv.do", true); //xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.responseType = "blob"; xhr.onload = function() { $.messager.progress('close'); if (xhr.getResponseHeader("Content-type") == 'application/octet-stream') { let url = window.URL.createObjectURL(xhr.response); let a = document.createElement("a"); a.href = url; a.style.display = 'none' a.download = fileName; a.click(); window.URL.revokeObjectURL(url); a.remove(); } else { let reader = new FileReader(); reader.addEventListener('loadend', function (e) { let data = JSON.parse(e.target.result); alert('提示', "系统出错,错误信息为:" + data.description + ",请将该信息提供给代维人员寻求帮助"); }); reader.readAsText(xhr.response); } }; let data = new FormData(); data.append('sql', getCsql(type)); data.append('col', col); data.append('title', title); data.append('separator', '|'); data.append('t', new Date().getTime() + ""); xhr.send(data); }

    3、方法二(两个接口)

    3.1、后台

    3.1.1、生成文件接口

    /**
     * 根据sql导成csv;成功返回生成的文件名称,失败返回错误信息
     * @param sql 导出sql
     * @param col 字段信息,以逗号分隔
     * @param title 文件头信息,以逗号分隔
     * @param separator csv文件分隔符
     * @param baseServiceName
     * @return
     */
    @RequestMapping("exportForCsv2")
    @ResponseBody
    public CallResult<String> exportForCsv2(String sql, String col, String title, String separator) {
        logger.info("sql={}", sql);
        logger.info("col={}", col);
        logger.info("title={}", title);
        logger.info("separator={}", separator);
        logger.info("baseServiceName={}", baseServiceName);
        OutputStream os = null;
        CallResult<String> result = new CallResult<>();
        try {
            String fileName = "数据信息(" + DateUtil.getCurrentDateString("yyyyMMddHHmmsss") + ").xlsx";
            os = new FileOutputStream(new File(WebconfigFactory.getTempFolder() + "数据信息(" + DateUtil.getCurrentDateString("yyyyMMddHHmmsss") + ").xlsx"));
    
            getService().exportForCsv(os, sql, col, title, separator);//调用service生成文件内容,请自行实现
    
            result.setResult(fileName);
        } catch(Exception e) {
            result = new CallResult<>(-1, "发生异常");
            e.printStackTrace();
        } finally {
            FileUtil.close(os);
        }
    
        return result;
    }

    3.1.2、下载文件接口

    /**
     * 下载方法
     * 1.点击页面下载,需传参数:
     *         fileName:磁盘上文件的名称,必需
     *         filePath或pathType或webPath:
     *             文件路径,传其中之一参数即可;
     *             filePath是磁盘的绝对路径;
     *             pathType是路径类型,对应webconfig.properties中的key
     *             webPath是相对web应用的路径,如/spa
     *         fileRealName:下载保存后的文件名称,可选;如果为空则与fileName一样
     *   
     * 2.后台导出文件下载,需传属性:
     *         fileName:磁盘上文件的名称,必需
     *         fileRealName:下载保存后的文件名称,可选;如果为空则与fileName一样
     */
    @RequestMapping("download")
    public void download(HttpServletRequest request, HttpServletResponse response) {
        String filePath = "";
        String fileName = request.getParameter("fileName");
        String fileRealName = "";
        if (!Util.isNull(fileName)) {
            filePath = request.getParameter("filePath");
            if (Util.isNull(filePath)) {
                String webPath = request.getParameter("webPath");
                if (Util.isNull(webPath)) {
                    String pathType = request.getParameter("pathType");
                    filePath = WebconfigFactory.getValue(pathType);
                } else {
                    //filePath = request.getRealPath(webPath) + System.getProperty("file.separator");
                    filePath = request.getServletContext().getRealPath(webPath) + System.getProperty("file.separator");
                }
            }
            fileRealName = request.getParameter("fileRealName");
        } else {//后台导出文件下载
            fileName = (String) request.getAttribute("fileName");
            filePath = WebconfigFactory.getTempFolder();
            fileRealName = (String) request.getAttribute("fileRealName");
        }
        
        if (Util.isNull(fileRealName)) {
            fileRealName = fileName;
        }
        
        BufferedInputStream bis = null;
        OutputStream out = null;
        try {
            String userInfoHeader = "User-Agent";
            String firefoxName = "firefox";
            String urlFileName = "";
            if (request.getHeader(userInfoHeader).toLowerCase().indexOf(firefoxName) > 0) {
                urlFileName = new String(fileRealName.getBytes("UTF-8"), "ISO8859-1");
            } else {
                urlFileName = java.net.URLEncoder.encode(fileRealName, "UTF-8");
            }
            
            response.reset();
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename="" + urlFileName + """);
            response.setHeader("Connection", "close");
            
            bis = new BufferedInputStream(new FileInputStream(filePath + fileName), BUFFER_SIZE);
            out = new BufferedOutputStream(response.getOutputStream(), BUFFER_SIZE);
            byte[] buf = new byte[BUFFER_SIZE];
            int len;
            while ((len = bis.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            FileUtil.close(bis);
            FileUtil.close(out);
        }
    }

    3.2、前台

    function exp() {
        let sql = "";//请自行设置
        let col = "";//请自行设置
        let title = "";//请自行设置
        $.messager.progress({title : '处理中...'});//easyui控件,可用其他控件
        $.getJSON(
            "${pageContext.request.contextPath}/common/export/exportForCsv.do",
            {
                sql : sql,
                col : col,
                title : title,
    separator: "|", t :
    new Date().getTime() }, function(data) { $.messager.progress('close');//easyui控件,可用其他控件 if (data.returnCode == 0) { location.href = "${pageContext.request.contextPath}/common/upDownload/download.do?fileName=" + encodeURI(data.result) + "&fileRealName=" + encodeURI("数据.csv") + "&pathType=path.temp.folder"; } else { $.messager.alert('提示', "系统出错,错误信息为:" + data.description + ",请将该信息提供给代维人员寻求帮助"); } } ); }

    注意例子里sql是从台传到后台,这样容易引起SQL注入攻击;严谨的做法应该是前台传参数,后台拼sql,并使用PrepareStatement来执行sql。

  • 相关阅读:
    FLASH开发[00]
    slickedit使用小技巧
    网络编程[34]
    网络编程[39]
    网络编程[41]
    网络编程[37]
    网络编程[32]
    网络编程[35]
    Apache安装和配置
    网络编程[40]
  • 原文地址:https://www.cnblogs.com/wuyongyin/p/13810767.html
Copyright © 2020-2023  润新知