前言
业务场景:当我点击一个按钮时,调用第三方接口,后台返回一个字节流,然后浏览器弹出文件另存为的弹窗,直接保存。
但是在实际操作当中,浏览器拿到返回的字节流是不能解析的,下载的文件也是一个乱码文件,所以我决定在前端对返回的文件进行解析完成下载。但是是这样做有个问题,后面会解释
前端解析字节流下载文件
经过我多方查询资料,发现大部分都是使用xhr,因为后台传回来的是字节流,所以不能用ajax方法(dataType没有能接受字节流的参数),要用原生的XMLHttpRequest。
前端代码如下所示
var xhr = new XMLHttpRequest();
var fileName = "文件名";//设置下载时候的文件名
//要请求的Url和携带的参数
var url = "";
xhr.open("get", url, true);//打开请求
//设置响应类型为blob类型(接受字节流)
xhr.responseType = "blob";
xhr.onload = function () {//返回成功之后执行的方法
if (this.status == "200") {
--------下载的部分开始-----
//获取响应文件流
var blob = this.response;
//将文件流保存到a标签
self.$(".targetLi a").attr("href", window.URL.createObjectURL(blob));
self.$(".targetLi a").attr("download",fileName);
self.$(".targetLi a span").click();
$("a").remove();
--------下载的部分结束-----
} else if (this.status == "500"){
//链接响应失败
}}
xhr.send();//发送请求
下载部分
(1)用window.URL.createObjectURL(blob)方法讲后台返回的字节流转换成URL,然后在页面上创建一个不可见的a标签(display:none)。a标签里面嵌多一层span元素,用于触发点击事件(等下告知为什么)。将转化好的URL赋值给a标签的href属性。ps:如果你创建的a标签外面的父元素有点击事件,要记得给a标签加上阻止冒泡[event.stopPropagation(); ]。
(2)最后通过.click();方法触发a标签的点击。但是我之前尝试过直接调用a标签.click();来触发点击事件,但是没有触发到,后来通过点击a标签里面span,通过事件冒泡的方式来触发a标签的点击事件$(“a span”).click();
(3)结束完以上操作后,用remove()方法,把a标签移除掉。
(4)保存文件的弹窗的出现。
当我们点击下载按钮时,问题出现了,跨域。所以我改变思路,在后端处理返回的字节流,直接返回response
后端处理返回字节流,完成下载
public HttpServletResponse downloadForString(HttpServletResponse response,String fileId,String type) {
String fileName = SysTools.getGUID()+"文件."+type;
//String context = HttpUtil.get(download+fileId);
byte[] buffer = HttpUtil.downloadBytes("url"+ fileId);
try {
//byte[] buffer = new String(context).getBytes();
// 清空response
response.reset();
// 设置response的Header
response.addHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("utf-8"),"ISO8859-1"));
response.addHeader("Content-Length", "" + buffer.length);
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
toClient.write(buffer);
toClient.flush();
toClient.close();
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
return response;
}
至此完成下载