前端下载文件(导出)的三种方式
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;
写在最后
哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!
相关推荐:
本文来自博客园,作者:Marydon,转载请注明原文链接:https://www.cnblogs.com/Marydon20170307/p/17115732.html