您的两个问题(this 和 the other one)都是 XY problem 的示例。
在寻找特定技术、技巧或技巧之前,请先明确定义您的目标:
目标
- 下载文件而不移动到其他页面;
- 同时,显示一个指示器(进度条、动画 gif、叠加层等...);
- 下载文件后,隐藏指示器。
解决方案
将您的触发器绑定到一个javascript函数as described here:
<a href="javascript:myJsFunction();"> download </a>
在您的 Javascript 函数中:显示指示器,启动计时器以检查下载是否结束(然后隐藏指示器),然后下载文件:
function myJsFunction(){
$("#containerWithGifImage").fadeIn("fast"); // Show the indicator
setInterval(function(){
$.ajax({
url : "/isDownloadFinished.action", type : "GET",
success : function(data,textStatus,jqXHR) {
$("#containerWithGifImage").fadeOut("fast"); // Hide the indicator
},error : function(jqXHR, textStatus, errorThrown) {
console.log("Download is still in progress, do nothing...");
}
});
}, 1000); // Check every second if download has finished
window.location='/download.action'; // Start the download
}
Download.action 必须在会话中添加一个属性,指示下载已开始,并在下载结束时更新。
由于使用stream 结果,您将响应的控制权委托给浏览器(因此在完成后您无法运行代码),您可以直接写入响应,然后返回NONE、@987654324 @:
public class Download extends ActionSupport implements SessionAware, ServletResponseAware {
@Setter private Map session;
@Setter private HttpServletResponse response;
public String execute(){
ServletOutputStream os = null;
try {
session.put("DOWNLOAD_STATUS","active");
response.setContentType("myContentType");
response.addHeader("Content-Disposition", "attachment; filename=\"foo.bar\"");
os = response.getOutputStream();
IOUtils.copy(getMyFileInputStreamSomeHow(), os);
} finally {
IOUtils.closeQuietly(os);
session.put("DOWNLOAD_STATUS","finished");
}
return NONE;
}
}
您还可以通过指定 Content-Length 响应标头 (response.setHeader("Content-Length", 1337);) as described here 让浏览器为您绘制进度条,您还可以在其中看到类似的机制来防止并发下载。
在IsDownloadFinished.action中,需要勾选session属性。如果它不存在或与已完成不同,则表示下载尚未开始或仍在进行中,因此什么也不做。否则,返回一个成功的 httpheader,这将使您的 jQuery $.ajax 函数运行success: 回调。您可以使用httpHeader 或json、as described here:
@Results({
@Result(name = ActionSupport.SUCCESS, type="httpheader", params = {"status", "200"}),
@Result(name = ActionSupport.ERROR, type="httpheader", params = {"error", "500"})
})
public class IsDownloadFinished extends ActionSupport implements SessionAware {
@Setter private Map session;
public String execute(){
if ("finished".equals(session.get("DOWNLOAD_STATUS")) {
session.remove("DW_STATUS");
return SUCCESS;
}
return ERROR;
}
}
这个问题有不同的解决方案,我向您展示了最简单的一个。
更优雅和复杂的解决方案将涉及长期持有的请求和Comet techniques(阅读:WebSocket),但我想您可以从这个启动示例中的轮询计时器开始,根据您的需要对其进行自定义,最终在对论点更满意时对其进行改进。