[英]Problem in response from servlet output stream
在基于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
我承认我不确定这个问题的根本原因。 根据迄今为止发布的代码,无法解释此行为。 SSCCE肯定会提供更多帮助。 但是我发现了这个问题的几种潜在原因。 也许修复其中一个或全部将解决具体问题。
您已将JSF的FacesContext
分配为bean的属性。 这是不好的 ,如果它的作用域比请求作用域更广,那肯定是不好的 。 应该始终通过FacesContext#getCurrentInstance()
在本地方法范围内获取它。 它的返回值即是线程局部变量,不应在其他请求之间共享。 也许您已将bean置于会话范围内,并且先前请求的悬挂response
对象(已设置标头)将被重用。
您没有在close()
方法上捕获IOException
。 如果客户端取消下载,则servletOutputStream.close()
将抛出IOException
指示客户端已中止响应。 在您的情况下,将不再关闭in
,并且不再删除tempFile
,并且将不再完成JSF响应。 您还应该捕获close()
并记录/忽略异常,以确保finally
完成工作。 也许tempFile
的存在会对您将来的POST操作产生影响。
您正在使用<h:commandLink>
而不是<h:outputLink>
或<h:link>
或普通<a>
进行页面间导航。 这使用POST而不是GET,这不利于用户体验和SEO。 您应该使用GET链接而不是POST链接。 另请参见何时应使用h:outputLink代替h:commandLink?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.