Unable to download zip file from JSF page
I am trying to download multiple PDF files as one zip file and then update the details on a JSF page - effectively showing that I am working on these files. I have achieved this using two requests behind the scene - 1) to update the DB details and refresh the screen 2) to download the zip file.
This works fine in single workstation windows environment, but the moment I deploy this in Linux environment, behind a load balancer, I get a blank page while trying to download the zip. I have written SOP stats to print the size of the file that is being sent to the ServletOutputStream via the JSF BB and I find that the right file sizes are being printed. But somehow I keep losing the zip as well as the updated JSF. This scenario also occurs randomly in Windows, which makes me worried :(. Please provide your valuable suggestions and help me out of this issue.
Some points that you might think for consideration: I am using Richfaces 3.3.3 Final, IE 8 browser, response transmission encoding type is chunked.
==== The BB method is as given below:
String checkoutDoc = service.checkout(docId,true,contract, error);
FacesContext ctx = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) ctx.getExternalContext().getResponse();
File tempPdf = new File(checkoutDoc);URI tempURI = tempPdf.toURI();
URL pdfURL = tempURI.toURL();ServletOutputStream outstream =response.getOutputStream();
try
{
URLConnection urlConn = pdfURL.openConnection();
response.setContentType("application/zip");
response.setHeader("Transfer-Encoding", "chunked");
response.addHeader("Content-disposition", "attachment;filename="+docId.toString()+".zip" );
BufferedInputStream bufInStrm = new BufferedInputStream (urlConn.getInputStream());
int readBytes = 0;
int bufferSize = 8192;
byte[] buffer = new byte[bufferSize];
while ((readBytes = bufInStrm.read(buffer)) != -1){
if (readBytes == bufferSize) {
outstream.write(buffer);
}else{
outstream.write(buffer, 0, readBytes);
}
outstream.flush();
response.flushBuffer();
}
bufInStrm.close();
}finally{
outstream.close();
}
FacesContext.getCurrentInstance().responseComplete();
}
The response headers that I captured using Firefox Http monitor are given below.
(Request-Line) POST /XXX/application开发者_运维百科/pages/xxx.xhtml HTTP/1.1
Host xxx.xxx.com
User-Agent Mozilla/5.0 (Windows NT 5.1; rv:5.0.1) Gecko/20100101 Firefox/5.0.1
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language en-us,en;q=0.5
Accept-Encoding gzip, deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection keep-alive
Referer http://xxx.com/xxx/application/pages/xxx.xhtml
Cookie JSESSIONID=E27C156AA37E5984073FAB847E4958D2.XXXX; fontSize=null; pageWidth=fullWidth
Content-Type multipart/form-data; boundary=---------------------------288695814700
Content-Length 1442
You should not be setting the Transfer-Encoding: chunked
header yourself if you are actually not writing out the file using chunked encoding yourself using for example ChunkedOutputStream
. The Servlet API will do this automatically whenever the response buffer is full and the response content length is unknown. However, whenever you've set this header yourself without actually writing out the body in chunked encoding, the behaviour is fully unspecified and dependent on the servletcontainer used.
Remove that header and let the Servlet API do its job. To improve performance (so that the Servlet API won't switch to chunked encoding when the response buffer is full), set the response content length header as well.
Having said that, your streaming approach is a bit clumsy. Massaging File
to URL
is unnecessary and the if-else
in the for
loop is unnecessary. May I suggest you the following?
// ...
File tempPdf = new File(checkoutDoc);
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
externalContext.setResponseContentType("application/zip");
externalContext.setResponseHeader("Content-Disposition", "attachment;filename=\"" + docId + ".zip\"");
externalContext.setResponseHeader("Content-Length", String.valueOf(tempPdf.length()));
Files.copy(tempPdf.toPath(), externalContext.getResponseOutputStream());
FacesContext.getCurrentInstance().responseComplete();
See also:
- How to provide a file download from a JSF backing bean?
精彩评论