<%
response.reset();
response.setHeader("Content-Disposition", "attachment;filename=\"" + "test.xls\"");
response.setHeader("Content-Transfer-Encoding", "binary");
response.setContentType("application/vnd.ms-excel");
InputStream is = new FileInputStream(realPath);
//OutputStream outStream = response.getOutputStream();
JasperPrint jasperPrint = JasperFillManager.fillReport(is,
parameters, new JRBeanCollectionDataSource(pdfList));
JRAbstractExporter exporter = new JExcelApiExporter();
exporter.setParameter(JExcelApiExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JExcelApiExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);
exporter.setParameter(JExcelApiExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
exporter.setParameter(JExcelApiExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
exporter.setParameter(JExcelApiExporterParameter.OUTPUT_STREAM, out);
exporter.exportReport();
outStream.flush();
outStream.close();
out.clear();
out =pageContext.pushBody();
%>
we use the code above to generate an excel, and it works well in tomcat + windows, but after we upload to linux + weblogic server, the excel is corrupted. I use text editor to open the excel, I found it add several empty line in the excel, which caused the excel can not be open successfully, anyone can point me the right direction ? Why there are space ? How it comes ?
Thanks in advance !
I suspect that your use of pageContext.pushBody() may be the culprit.
pushBody is, as far as I know, meant for updating the output in the scope of a JSP tag.
When you are generating binary content such as an excel file, where you need to be absolutely sure that the intended bytes, and only the intended bytes reach the browser, you need to write those bytes, flush the output, and then make sure that nothing else gets written. By invoking pushBody(), you are making it so that more content can be written to the output, and any blank lines (and the carriage returns / line feeds between them) in the JSP page could be output.
All in all, I suggest that you just not do this in a JSP - do it in a Servlet instead.
Related
I have a few Java servlets (3.x - Tomcat 8) running that generate and return PDF documents. I've never had any problems with any of them. I recently wrote a new servlet to also create and return a PDF document, and this new servlet is using the exact same piece of response code as the others are using:
response.reset();
response.setContentType("application/pdf");
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Disposition","attachment; filename=\""+filename+".pdf\"");
response.setContentLength(pdfBytes.length);
System.out.println("# Bytes => " + pdfBytes.length);
ServletOutputStream sos = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(sos);
bos.write(pdfBytes);
sos.flush();
sos.close();
As I said, this has been working fine with the others, but when I call the new servlet, it returns 0 bytes, even though the print statement above has a non-zero value.
However, if I change the response writing code above to:
OutputStream os = response.getOutputStream();
os.write(pdfBytes);
os.flush();
os.close();
...it works fine, returning a well-formed PDF document. Why might this be happening?
You're not flushing the BufferedOutputStream - so it's buffering all your data. You should flush that, not the ServletOutputStream.
However, if you're only writing a single byte array, there's no point in using BufferedOutputStream anyway - and you shouldn't need to explicitly flush anyway, as closing will flush. So you just need:
ServletOutputStream sos = response.getOutputStream();
sos.write(pdfBytes);
// Unclear whether *this* is needed, either.
sos.close();
I'd personally expect the servlet container to close the output stream, but it's not clear from the docs. Whether you want to close it if an exception occurs is a different matter...
you should really flush and close bos not sos
I use jasper reports version 6.2.1 with the following configuration:
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
JREmptyDataSource jasper = new JREmptyDataSource();
JasperPrint jasperPrint = jasperFillManager.fillReport(this.getClass().getClassLoader().getResource("/reports/tn2.jasper").getPath(), null, jasper);
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment" + "; filename=hehe.pdf");
ByteArrayOutputStream finalReport = new ByteArrayOutputStream();
JasperExportManager.exportReportToPdfStream(jasperPrint,finalReport);
PrintWriter ouputStream = response.getWriter();
ouputStream.write(new String(finalReport.toByteArray()));
ouputStream.flush();
FacesContext.getCurrentInstance().responseComplete();
I do it from my JSF 2.x backing bean.
But I always get a blank page when try to export to stream. But if I do:
JasperExportManager.exportReportToPdfFile(jasperPrint,
"d://hehe.pdf");
it works ok, I see the content in the generated file. How to force it work with streams? I tried to close/flush streams in different configurations, use ARM, etc. No luck so far
This part is wrong.
ByteArrayOutputStream finalReport = new ByteArrayOutputStream();
JasperExportManager.exportReportToPdfStream(jasperPrint,finalReport);
PrintWriter ouputStream = response.getWriter();
ouputStream.write(new String(finalReport.toByteArray()));
You're allocating a byte array in memory. Then you're exporting the report to it. Then you're converting the byte array to string (which is basically a character array). Then you're writing it to a character based writer. Basically, you've corrupted the binary content of the PDF file by converting all bytes to characters in a fairly inefficient and platform-dependent way. It's as if you're opening the PDF file in a text editor like Notepad and then saving it as a TXT file. Such a file is not anymore readable by a PDF reader.
You should just stream the bytes unmodified to the byte based output stream. Replace all of above by the below oneliner.
JasperExportManager.exportReportToPdfStream(jasperPrint, response.getOutputStream());
Unrelated to the concrete problem, since JSF 2.x, ExternalContext offers several delegate methods without the need to cast down to HttpServletResponse. See also How to provide a file download from a JSF backing bean? for a concrete example.
The solution is trivial:
FacesContext.getCurrentInstance().getExternalContext().responseReset();
And that's it!!
Thanks for your help.
I solved this by adding the next line:
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, new JREmptyDataSource());
I am using Grails.
I have a sample code to download an excel file:
XSSFWorkbook workbook = new XSSFWorkbook()
....
FileOutputStream out = new FileOutputStream(new File("C:\\excel.xlsx"))
workbook.write(out)
out.close()
Here the excel file will be downloaded automatically. I would want system to prompt the user to download the file in the browser window.
I tried using the below code:
response.setHeader("Cache-Control", "public")
response.setContentType("application/vnd.ms-excel")
response.setHeader('Content-Disposition', 'Attachment;Filename="excel.xlsx"')
ServletOutputStream outputStream = response.getOutputStream()
workbook.write(outputStream)
outputStream.flush()
outputStream.close()
Which doesn't work. How to achieve this?
Thanks in advance.
Quoted form W3C
Parameter values are normally case sensitive, but certain parameters are interpreted to be case- insensitive, depending on the intended use. (For example, multipart boundaries are case-sensitive, but the "access- type" for message/External-body is not case-sensitive.)
There for, try changing
response.setHeader('Content-Disposition', 'Attachment;Filename="excel.xlsx"')
to
response.setHeader('Content-Disposition', 'attachment;filename="excel.xlsx"')
I've a Servlet that makes and returns a zip file
Something like this
response.setHeader("Pragma","Public");
response.setHeader("Cache-Control","must-revalidate,post-check=0,pre-check=0");
response.setContentType("application/octet-stream");
response.setHeader("Expires", "0");
response.setHeader("Content-Transfer-Encoding", "binary");
if(file.getName().contains("–")){
response.setHeader("Content-Disposition", "inline; filename=\"file.zip\"");
}
OutputStream os = response.getOutputStream();
ZipOutputStream zos = new ZipOutputStream(os);
for (File f : files) {
//add files, it's working...
}
bis.close();
fis.close();
zos.closeEntry();
}
zos.flush();
zos.close();
os.flush();
os.close();
Currently to download I'm using a iframe, so I set src attribute to start download.
The download frame loads when call this function
function loadIDownloadFrame(url) {
document.getElementById("idownloadFrame").src=url;
}
But now, I need show a message after return the zip file without leaving current page. I need to know if the servlet returned the zip file.
I tried get iframe status with "window.frames['idownloadFrame'].document.readyState", but always status is "complete".
Anyone have any solutions?
I think you could directly refer to download link to mapped Servlet(which returns the downloadable binary). It would not change the page if you set appropriate header in the mapped servlet to return the binary, from the code snippet it looks like you are doing that already. Why do you have to have file download in a different iframe? You don't necessarily need that.
Where do you have to show the message? Like a JavaScript alert or something?
protected static byte[] exportReportToPdf(JasperPrint jasperPrint)
throws JRException {
JRPdfExporter exporter = new JRPdfExporter();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
exporter.setParameter(JRPdfExporterParameter.PDF_JAVASCRIPT,
"this.print({bUI: true,bSilent: false,bShrinkToFit: true});");
exporter.exportReport();
return baos.toByteArray();
}
We are using code like this to export a PDF document from a Jasper application.
The line
exporter.setParameter(JRPdfExporterParameter.PDF_JAVASCRIPT,
"this.print({bUI: true,bSilent: false,bShrinkToFit: true});");
adds JavaScript to send the PDF document directly to the printer.
The expected behavior is that a print dialog will come up with a preview of the PDF document.
This works fine most of the time - except I am having problems about one out of every 5-6 times in Internet Explorer 8 and Firefox.
What happens is - the print preview dialog with the PDF document does not appear or it appears with a blank document in the preview window.
-I've tried a number of different JavaScripts (different params to this.print() via exporter.setParameter
-I've tried setting different response headers such as
response.setContentType("application/pdf");
response.setHeader("Content-disposition","inline; filename=\""
+ reportName
+ "\"");
response.setContentLength(baos.size());
these did not seem to help
This seems to be an IE and FF issue. Has anyone ever dealt with this problem? I need to get it to work across all browsers 100% of the time. Perhaps a different approach to accomplish the goal of sending the PDF document export directly to the printer? or a third party library that will work across browsers?
Maybe it isn't getting a chance to update the UI. The following code delays the print perhaps giving it the chance it needs. I didn't test as I don't have your environment.
exporter.setParameter(JRPdfExporterParameter.PDF_JAVASCRIPT,
"app.setTimeOut('this.print({bUI: true,bSilent: false,bShrinkToFit: true});',200);")