最近做项目遇到了一个需求:
页面无刷新下载文件并在文件下载完成后在页面上提示用户文件下载完成。
在实现这个需求过程中会遇到两个问题:
1、要求页面无刷新,我们自然会想到ajax。但是有个问题,ajax请求服务器端返回来的是字符串形式的数据流,浏览器也会以字符串形式进行接收,即便服务器端将响应头设置成"Content-Disposition"依然不会触发浏览器的下载动作;
2、要求下载完成后在页面上提示用户文件下载完成。由于JavaScript代码是同步执行的,也就是说JavaScript在提交表单之后会立刻执行下面的代码,并不会阻塞去等待服务器端执行完下载任务。倘若要下载的文件的数据量很大,那么就会造成页面早已提示用户文件下载完成,但是实际上文件还在下载过程中。
下面我们就来一一解决上述两个问题。
1、页面无刷新除了ajax我们还可以灵活运用另外一种技术:iframe,来实现假的ajax,总之实现页面无刷新就可以了,用iframe以表单形式将请求发送给后台,这样只要后台服务器端将响应头设置成"Content-Disposition",返回前端就会触发浏览器的下载动作。附代码:
HTML:
<form action=“upload.nopv.nhn” method=“post” target=“ifrResult”>
<input type=“button” value=“download” οnclick=“download();”/>
</form>
<iframe id=“ifrResult” name=“ifrResult” style=“display:none;” noresize=“noresize”>
</iframe>
JavaScript:
function upload(){
document.forms[0].submit();
}
2、要使JavaScript进行异步操作就得用ajax技术,在回调函数里提示用户下载完成,但是ajax技术在上面1里已经被否决。既然JavaScript的执行不能阻塞以等到后台任务执行完,那么我们可以在前台启动一个任务来监听后台是否执行完毕。什么可以作为前端和后端交互信息的媒介呢?Cookie!我们可以在后端代码里设置一个cookie,cookie会随着服务器端的响应一同被带回到浏览器,然后前端JavaScript不停的检测这个cookie的值,如果检测到了,代表后台任务执行完成。这样就实现了后台执行完下载任务之后页面才能提示用户下载完成。附代码:
HTML:
<span id=“msg”></span>
JavaScript:
function upload(){
delCookie(“finished”); // 每次下载文件之前要把上次的cookie删除
document.forms[0].submit();
var timer = setInterval(function(){
if (checkCookie(“finished”)){
document.getElementById(“msg”).innerHTML = “下载完成”;
clearInterval(timer);
}
}, 500);
}
function delCookie(name){
if(checkCookie(name)){
document.cookie = name + “=; expires=Thu, 01 Jan 1970 00:00:01 GMT;”;
}
}
function checkCookie(name){
var cookieArray = document.cookie.split("; “);
for (var i = 0; i < cookieArray.length; i++){
var arr = cookieArray[i].split(”=");
if(arr[0] == name) return true;
}
return false;
}
Java:
HttpServletResponse response = ServletActionContext.getResponse();
Cookie cookie = new Cookie(“finished”, “1”);
response.addCookie(cookie);
至此需求基本实现。但是为了严谨起见,考虑到同一个浏览器的多个页面同时点击下载,由于这多个页面共享cookie,会出现一个页面下载完成而另一个页面还没下载完就提示用户文件下载完成的情况。解决这个小问题只需在前端把当前时间传到后台即可。
HTML里加个隐藏域:<input type=“hidden” name=“timeinfo”/>
JavaScript修改两处:
delCookie(“finished” + document.forms[0].timeinfo.value);
if (checkCookie(“finished” + document.forms[0].timeinfo.value))
后台Java修改一处:
Cookie cookie = new Cookie(“finished” + ServletActionContext.getRequest().getParameter(“timeinfo”), “1”);