在我的基于Java的Web应用程序中,我试图在ZIP文件中写一些文件,我想提示用户下载/取消/保存。下载对话框打开的时间以及如果我单击取消,之后如果我尝试访问应用程序中的任何链接,则会再次打开对话框。这是我的代码片段。
private void sendResponse(byte[] buf, File tempFile) throws IOException {
long length = tempFile.length();
HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
String disposition = "attachment; fileName=search_download.zip";
ServletOutputStream servletOutputStream = null;
InputStream in = null;
try {
if (buf != null) {
in = new BufferedInputStream(new FileInputStream(tempFile));
servletOutputStream = response.getOutputStream();
response.setContentType("application/zip");
response.setHeader("Content-Disposition", disposition);
while ((in != null) && ((length = in.read(buf)) != -1)) {
servletOutputStream.write(buf, 0, (int) length);
}
}
} finally {
if (servletOutputStream != null) {
servletOutputStream.close();
}
if (in != null) {
in.close();
}
if (tempFile != null) {
tempFile.delete();
}
}
context.responseComplete();
}
此外,一旦我点击“保存/打开”,它就会按预期工作。我希望清除响应对象的问题。请帮我提供一些解决方案。
修改 下载选择的方法
public void downloadSelected() throws IOException {
List<NodeRef> list = init();
StringBuffer errors = new StringBuffer("");
ZipOutputStream out = null;
File tempFile = null;
byte[] buf = null;
try {
if (list != null && list.size() > 0) {
tempFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX_ZIP);
out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempFile)));
buf = writeIntoZip(list,out);
sendResponse(buf,tempFile);
} else {
errors.append("No Items Selected for Download");
this.errorMessage = errors.toString();
}
}
catch(IOException e) {
System.out.println("Cancelled");
}
}
写入Zip方法:
private byte[] writeIntoZip(List<NodeRef> list,ZipOutputStream out) throws IOException {
String downloadUrl = "";
InputStream bis = null;
Node node = null;
String nodeName = "";
byte[] buf = null;
Map<String,Integer> contents = new HashMap<String, Integer>();
ContentReader reader = null;
for (NodeRef nodeRef : list) {
try {
node = new Node(nodeRef);
nodeName = node.getName();
reader = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getContentService().getReader(nodeRef, ContentModel.PROP_CONTENT);
bis = new BufferedInputStream(reader.getContentInputStream());
if (bis != null) {
contents = setFiles(contents,nodeName);
nodeName = getUniqueFileName(contents, nodeName);
buf = new byte[4 * 1024];
buf = writeOutputStream(bis).toByteArray();
out.putNextEntry(new ZipEntry(nodeName));
out.write(buf);
}
} catch (Exception e) {
e.printStackTrace();
if(out != null) {
out.close();
}
}
}
if(out != null) {
out.close();
}
return buf;
}
谢谢, Jeya
答案 0 :(得分:1)
我当然不确定这个问题的根本原因。基于到目前为止发布的代码,无法解释此行为。 SSCCE肯定会有所帮助。但我发现了这个问题的几个潜在原因。也许修复其中一个或全部将解决具体问题。
您已将JSF的FacesContext
指定为bean的属性。这是错误,如果它是一个范围比请求范围更广泛的bean。它应该始终在FacesContext#getCurrentInstance()
的本地方法范围内获取。它返回一个线程局部变量,永远不应该在其他请求之间共享。也许你已经将bean放在会话范围内,并且已经设置了标题的前一个请求的悬空response
对象将被重用。
您没有抓住IOException
方法close()
。如果客户端取消下载,则servletOutputStream.close()
将抛出IOException
,表示客户端已中止响应。在您的情况下,in
将不再关闭,tempFile
将不再被删除,JSF响应将不再完成。您还应该捕获close()
并记录/忽略该异常,以便确保finally
完成其工作。也许tempFile
的存在会对您未来的POST操作产生影响。
您正在使用<h:commandLink>
代替<h:outputLink>
或<h:link>
或普通<a>
进行页面到页面的导航。这使用POST代替GET,这对于用户体验和SEO来说是糟糕。您应该使用GET链接而不是POST链接。另请参阅When should I use h:outputLink instead of h:commandLink?