Problem in response from servlet output stream
In my Java Based Web application, I am trying to write some files in a ZIP file and I want to prompt the user to download/cancel/save. The time when the download dialog box opens and if I click on cancel, after then if I try to access any links in my application then开发者_高级运维 the dialog box opens again. Here's my code snippet.
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();
}
Also once I click on save/Open, its working as expected. I hope the problem in clearing the response object. Please help me in providing some solutions.
EDIT downloadSelected Method
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");
}
}
Write into Zip method:
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;
}
Thanks, Jeya
I'm admittedly not sure of the root cause of this problem. This behaviour is not explainable based on the code posted as far. An SSCCE would definitely help more. But I spot several potential causes of this problem. Perhaps fixing one or all of them will fix the concrete problem.
You've assigned JSF's
FacesContext
as a property of the bean. This is bad and definitely if it's a bean with a broader scope than the request scope. It should always be obtained inside the local method scope byFacesContext#getCurrentInstance()
. It's returns namely a thread local variable and should never be shared among other requests. Perhaps you've put the bean in the session scope and a danglingresponse
object of the previous request with the headers already set will be reused.You are not catching the
IOException
onclose()
methods. If the client cancels the download, then theservletOutputStream.close()
will throw anIOException
indicating that the client has aborted the response. In your case, thein
won't be closed anymore and thetempFile
won't be deleted anymore and the JSF response won't be completed anymore. You should also catch theclose()
and log/ignore the exception so that you can ensure that thefinally
finishes its job. Perhaps the presence oftempFile
has consequences for your future POST actions.You are using
<h:commandLink>
instead of<h:outputLink>
or<h:link>
or plain<a>
for page-to-page navigation. This uses POST instead of GET which is bad for user experience and SEO. You should use GET links instead of POST links. See also When should I use h:outputLink instead of h:commandLink?
精彩评论