前端下载文件(导出)的三种方式

1.情景展示

在开发过程中,我们经常会遇到文件下载(图片等)和文件导出(excel等)功能;

除了后端层面之外,前端如何进行请求的发送与接收返回的文件流(数据流)?

2.具体分析

通过a标签来实现。

3.解决方案

前提条件:我想要下载一个文件,并且后端已经提供好了接口。

方式一:最简单

条件1:在发送请求前已经拿到了请求地址,并且确定要传的参数;

条件2:一般情况下,后端是要前端发送请求参数的,我们可以通过get请求携带过去就可以了。

但是,需要注意:参数值不能过长(不能超过get请求允许的URL长度字节)。

最简单,不用携带参数,直接访问文件所在地址;

<a href="http://127.0.0.1:8080/demo/download/test.zip" target="_blank">下载插件</a>

不访问文件地址,携带较少参数。

<a href="http://127.0.0.1:8080/demo/download.do?path=WEB-INF/uploadFile&name=aa.png" target="_blank">下载图片</a>

方式二:不常见

不与后台进行交互,直接从页面上取数据进行导出。

通过js创建a标签;

根据数据逆向生成URL对象;

将a标签添加到页面上,并触发点击事件,从而实现下载功能。

// 导出SQL语句
this.exportSql = function () {
	let sqlText= $('#sqlText').val();
	if (sqlText == '') {
		Dialog.Alert('消息提示', '请录入查询语句');
		return;
	}
	
	let url = URL.createObjectURL(new Blob([sqlText]));
	let a = document.createElement('a');
	a.href = url;
	a.download = 'sql_' + new Date().getTime() +'.sql';
	document.body.appendChild(a);
	a.click();// 通过点击事件触发下载
	document.body.removeChild(a);
};

方式三:常见


// 导出查询语句
this.export = function() {
	var exportFormat= $('#exportFormat').val();
	if (exportFormat == '') {
		Dialog.Alert('消息提示', '请选择导出格式');
		return;
	}
	
	var sqlText= $('#sqlText').val();
	if (sqlText == '') {
		Dialog.Alert('消息提示', '请录入查询语句');
		return;
	}
	
	var ListContainerHTML= $('#ListContainer').html();
	if (ListContainerHTML == '') {
		Dialog.Alert('消息提示', '请录入查询语句并执行');
		return;
	}
	
	// 添加Cookie
	var cookie = new Cookie();
	var sqlCookieName = new Date().getTime();
	
	cookie.SetCookie("typlat" + sqlCookieName, sqlText, 1);// 有效期:1天
	
	var DATABASEID = $('#DATABASEID').val();
	var exportFormat = $('#exportFormat').val();
	let suffix;
	if ('excel' == exportFormat) {
		suffix = '.xls';
	} else if ('csv' == exportFormat) {
		suffix = '.csv';
	} else if ('json' == exportFormat) {
		suffix = '.json';
	} else {
		
	}
	
	let param = 'sqlName=' + sqlCookieName + '&DATABASEID=' + DATABASEID + '&exportFormat=' + exportFormat;
	let url = baseUrl + '/metadata/sql/viewList2.do?' + param;
	let a = document.createElement('a');
	a.href =  url;
	a.download = exportFormat + '_' + sqlCookieName + suffix
	
	document.body.appendChild(a); 
	a.click();// 通过点击事件触发下载
	document.body.removeChild(a);
	
};

数据通过js拿到,将要传到后台的参数;

大数据不能通过get请求进行传送,我们可以通过cookie来实现数据的携带;

后续流程与方式二一致。 

4.拓展

new Blob(array, options)

URL.createObjectUrl(object)

 

5.js导出文本的三种方式

方式一:根据文字生成URL对象完成下载;

let url = URL.createObjectURL(new Blob(["需要导出的文字"]));
let a = document.createElement('a');
a.href =url;
a.download = '文件名.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);

方式二:通过a标签指定文本格式和编码直接下载;

function downloadTxt(fileName, content) {
    let a = document.createElement('a');
    a.href = 'data:text/plain;charset=utf-8,' + content;
    a.download = fileName;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
}

方式三:通过FileReader转化为base64字符串下载。

function downloadByBlob (fileName, content) {
    let blob = new Blob([content], {
        type: "text/plain;charset=utf-8"
    });
    let reader = new FileReader();
    reader.readAsDataURL(blob);
    reader.onload = function(e) {
        let a = document.createElement('a');
        a.download = fileName;
        a.href = e.target.result;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }
}

6.后端代码实现

主要是,根据不同的格式,生成对应的文件流或字符流;

然后将输出流返回给前端。

查看代码
try {
    // Step5:将原始数据,转成对应的格式,并以流的形式输出。
    String exportFormat = paramsMap.get("exportFormat");

    response.setCharacterEncoding("utf-8");
    
    // 导出格式
    switch (exportFormat) {
    case "excel":
        // Step6:获取所有查询字段
        // 排序Map
        SortedMap<String, String> columnMap = new TreeMap<>();
        // 取第一行数据
        Map rowMap = (Map) resultlist.get(0);
        
        String columnName;
        // 拿到查询的字段名,并排序
        for (Iterator i = rowMap.keySet().iterator(); i.hasNext();) {
            columnName = String.valueOf(i.next());
            columnMap.put(columnName, columnName);
        }
        
        // 删除行号字段
        if ("1".equals(DATABASETYPE)) {// oracle
            columnMap.remove("ROWNO");
        }
        
        List<String> columnList = new ArrayList<>();
        // 塞进list
        for (Iterator i = columnMap.keySet().iterator(); i.hasNext();) {
            columnList.add(String.valueOf(i.next()));
        }
        
        // 标题行
        String[] title = (String[]) columnList.toArray(new String[0]);
        
        String[][] values = new String[resultlist.size()][];
        
        for (int i = 0; i < resultlist.size(); i++) {
            values[i] = new String[title.length];
            // 将对象内容转换成string
            Map obj = (Map) resultlist.get(i);
            
            for (int j=0; j < title.length; j++) {
                Object cell = obj.get(title[j]);
                if(cell != null) {                
                    values[i][j] = cell.toString();
                } else {
                    values[i][j] = "";
                }
            }
        }
        
        HSSFWorkbook wb = ExcelUtils.getHSSFWorkbook("查询结果导出", title, values, null);
        
        response.setContentType("application/vnd.ms-excel");// .xls
        // 文件名,由前端a标签的download属性来指定
//				response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("医共体预算拨付情况.xls", "UTF-8"));
        // 读取目标文件,通过response将目标文件写到客户端
        out = response.getOutputStream();
        wb.write(out);
        
        break;
    case "csv":
        StringBuilder data = CsvUtils.generateCsv(null, resultlist);
        
        response.setContentType("application/csv");// .csv
        // 文件名,由前端a标签的download属性来指定
//				response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("医共体预算拨付情况.csv", "UTF-8"));
        // 读取目标文件,通过response将目标文件写到客户端
        out = response.getOutputStream();

        //添加bom,不加Excel打开中文会乱码
        out.write(new byte[] { (byte) 0xEF, (byte) 0xBB,(byte) 0xBF });
        out.write(data.toString().getBytes("UTF-8"));
        break;
    case "json":
        JSONArray ja = new JSONArray(resultlist);
        byte[] byte1 = ja.toJSONString().getBytes(StandardCharsets.UTF_8);
        in = new ByteArrayInputStream(byte1);
        
        // 设置文件MIME类型
        response.setContentType("application/json");
        // 读取目标文件,通过response将目标文件写到客户端
        out = response.getOutputStream();
        
        // 写文件
        int b;
        while ((b = in.read()) != -1) {
            out.write(b);
        }
        
        break;
    default:
        throw new Exception("未支持的导出格式!");
    }
		
} catch (Exception e) {
    this.code = -1;
    this.msg = e.getMessage();
    this.expMsg = getExceptionMessage(e);
    log.error(e.getMessage());
    return getResult();
} finally {
    // 使当前cookie失效
    sqlNameCookie.setMaxAge(0);
    // cookie的生效路径为当前请求路径(不设置的话,默认用的就是原来的路径)
    sqlNameCookie.setPath(sqlNameCookie.getPath());
    response.addCookie(sqlNameCookie);
    // 关闭流
    try {
        if (null != in) {
            in.close();
        }
        if (null != out) {
            out.flush();
            out.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
return null;

 

写在最后

  哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!

 相关推荐:

posted @ 2023-02-13 11:26  Marydon  阅读(7215)  评论(0编辑  收藏  举报