Jasper Report not streamed correctly in PDF - jsf

I am generating a PDF invoice with JR. On my local machine (linux ubuntu) works perfectly:
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
String templateAbsolutePath = ec.getRealPath(templateRelativePath);
JasperReport jasperReport;
try {
jasperReport = JasperCompileManager.compileReport(templateAbsolutePath);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, getParametriFattura(fattura), datasource );
JasperExportManager.exportReportToPdfStream(jasperPrint, ec.getResponseOutputStream());
} catch (JRException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
fc.responseComplete();
However, when I deployed my war to the staging server (linux ubuntu) it shows that:
I suppose it's a trivial problem, but where can I start from?
I deliberately omitted configurations, system details and so on... because I don't know what can be useful.

You need to explicitly tell the webbrowser that it's a PDF file, not a (X)HTML file.
ec.setResponseContentType("application/pdf");
Note: this needs to be set before any bit is written to the response body.

Related

Binary file download generated in backing bean appears as plain text in client [duplicate]

This question already has answers here:
How to provide a file download from a JSF backing bean?
(5 answers)
Closed 7 years ago.
I have server side webapp and I am trying to generate an excel file with Apache POI, which is provided as download by backing bean. I am able to write the file on server disk, but not to write it to HTTP response.
Here's my code
public void createExcel(){
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
HSSFWorkbook workBook = new HSSFWorkbook();
HSSFSheet sheet = workBook.createSheet("hello world");
HSSFRow row = sheet.createRow((short) 0);
HSSFCell cell;
cell = row.createCell(0);
cell.setCellValue(new HSSFRichTextString("Hello"));
cell = row.createCell(1);
cell.setCellValue(new HSSFRichTextString("world"));
workBook.write(bos);
bos.flush();
FacesContext fc = FacesContext.getCurrentInstance();
downloadExcel(bos, fc);
} catch (FileNotFoundException e) {
log.debug("file not found ", e);
} catch (IOException e){
log.debug("IOException ", e);
}
}
public void downloadExcel(ByteArrayOutputStream baos, FacesContext fc){
HttpServletResponse response = (HttpServletResponse)fc.getExternalContext().getResponse();
response.setContentType("application/vnd.ms-excel");
response.addHeader("Content-Disposition", "attachment; filename=testxls.xls");
try {
ServletOutputStream out = response.getOutputStream();
baos.writeTo(out);
out.flush();
out.close();
} catch (IOException e) {
log.error("IOException ", e);
}
fc.responseComplete();
}
And the result is following
This happens in all browsers I tried: Chrome, FF and IE. I hope you can point out what I am missing here.
Finally found the solution in BalusC given link. I called createExcel() method in JSF inside <a4j:commandButton> tag, which called method through ajax. Changed it to <h:commandButton> and now it is working.
The browser does not ever open a dialog to invite you to download the file when you navigate your servlet? I guess the reason is that you missed to enclose the filename between quotes:
response.addHeader("Content-Disposition", "attachment; filename=\"testxls.xls\"");

bytes of file download appear as plain text instead of as actual attachment

I think I have put all the pieces together to provide download file. Instead, below code is writing byte array on browser itself. I am sure the byte [] do not have any problem as I used FileOutputStream earlier to generate the excel file.
FacesContext fc = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)fc.getExternalContext().getResponse();
InputStream templateInputStream=null;
try {
templateInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(SOME_PATH);
if(templateInputStream==null) {
throw new WebException("Template not found at: "+SOME_PATH);
}
byte[] suppData= ExcelRenderer.render(templateInputStream, data);
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition","attachment;filename=\""+REPORT_FILE_NAME+"\"");
response.getOutputStream().write(suppData);
response.flushBuffer();
fc.responseComplete();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
if (templateInputStream!=null){
templateInputStream.close();
}
}

How to display PDF in Web browser using ServletOutputStream

I have been trying to display a pdf report in a web browser using ServletOutputStream. So far, I am able to download the file but I cannot get it to display in the browser. Here is my code
private void initReport()
{
DBConnection connection = new DBConnection();
try {
jasperPrint = JasperFillManager.fillReport(getContext().getExternalContext().getRealPath("/WEB-INF/reports/testReport.jasper"), new HashMap(),connection.getConnection());
} catch (JRException ex) {
Logger.getLogger(ReportBacking.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void showPDF()
{
initReport();
try {
HttpServletResponse httpServletResponse;httpServletResponse = (HttpServletResponse) getContext().getExternalContext().getResponse();
httpServletResponse.setContentType("application/pdf");
httpServletResponse.addHeader("Content-disposition", "inline;filename=testReport.pdf");
ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
JasperExportManager.exportReportToPdfStream(jasperPrint,servletOutputStream);
getContext().responseComplete();
getContext().renderResponse();
} catch (JRException | IOException ex) {
Logger.getLogger(CertificateApplicationAddBacking.class.getName()).log(Level.SEVERE, null, ex);
getContext().addMessage(null, new FacesMessage("Could not generate report"));
}
If I change the content-diposition to attachment. I am able to download the file.
Whether it downloads or displays in the browser is out of your control. Its a setting in the browser or in Acrobat, or a combination, which only the user can change (if they know how).
However, you could use a Flash/Silverlight app that let's you load a PDF into it, or find a way to use PDF.js by passing your PDF bytes from the server to PDF.js and having it render the PDF in the browser. Look at the examples page. Where they do:
PDFJS.getDocument('helloworld.pdf').then(function(pdf) {
// you can now use *pdf* here
});
You can change 'helloworld.pdf' to the URL of your servlet.

Printing jsf ArrayLIst via Jasper Report using JRBeanCollectionDataSource

I'm using a jsf array list to retrieve records based on some criteria. Now I want contents of this array list to be print via Jasper report. I followed following steps.
Used empty datasource in Jasper report.
Created Fields in jasper reports as those of my bean properties.
Then I compiled the report to employeeList.jasper and placed that in my /reports
/employeeReports/employeeList.jasper folder in jsf project (I'm using Eclipse).
I have then used following code to fill and show the pdf report
JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(this.srchdEmployeesList);
try {
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response =
(HttpServletResponse) facesContext.getExternalContext().getResponse();
HashMap parameterMap = new HashMap();
JasperFillManager.fillReportToFile("/reports/employeeReports/employeeList.jasper", parameterMap, dataSource);
InputStream reportStream = facesContext.getExternalContext().getResourceAsStream("/reports/employessReports/employeeList.jasper");
ServletOutputStream servletOutputStream = response.getOutputStream();
Class.forName("oracle.jdbc.driver.OracleDriver");
Session hibernateSession = null;
hibernateSession = HibernateUtils.currentSession();
connection = hibernateSession.connection();
facesContext.responseComplete();
response.setContentType("application/pdf");
response.setHeader("Content-disposition", "attachment;");
JasperRunManager.runReportToPdfStream(reportStream, servletOutputStream, parameterMap, connection);
connection.close();
servletOutputStream.flush();
servletOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
I recieve following error message and report do not show up
net.sf.jasperreports.engine.JRException: java.io.FileNotFoundException: \reports
\employeeReports\employeeList.jasper
Caused by: java.io.FileNotFoundException: \reports\employeeReports
\employeeList.jasper ... 34 more
Help in this regard would be highly appreciated. Bundles of thanks in advance.
You wrote that the report is in "Web Content/reports/employeeReports/employeeList". But your code doesn't seem to point there. Your code points to "/reports/employeeReports/employeeList.jasper".
Is it as simple as using a relative path?
"reports/employeeReports/employeeList.jasper" or "./reports/employeeReports/employeeList.jasper"

Export File : Faces Servlet threw java.lang.IllegalStateException: Cannot forward after response has been committed

I have got a method which exports xls file. The method works correctly but after it's excuted, I get
Faces Servlet threw java.lang.IllegalStateException: Cannot forward after response has been committed
Here is the method:
public String exportXls() {
OutputStream out = null;
try {
FacesContext cxt = FacesContext.getCurrentInstance();
ExternalContext context = cxt.getExternalContext();
HttpServletResponse response = (HttpServletResponse) context.getResponse();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=name.xls");
out = response.getOutputStream();
WritableWorkbook workbook = createWorkbook(response);
// All sheets and cells added. Now write out the workbook
workbook.write();
workbook.close();
System.out.println("XLS written!");
} catch(Exception ex) {
logger.error("ERROR OCCURRED WITH exportXls" + ex.toString());
} finally {
try {
if (out != null) out.close();
} catch(IOException e) {
logger.error(e);
e.printStackTrace();
}
}
return "page";
}
I have closed the OutputStream and Workbook. What do you think could be the problem?
You need to tell JSF that you've already completed the response by calling FacesContext#resposneComplete(). You should also not return a navigation case string (not that it would harm, but that's entirely superfluous and confusing for the maintainer).
public void exportXls() {
// ...
FacesContext.getCurrentInstance().responseComplete();
}
Unrelated to the concrete problem, I strongly recommend to remove the catch (Exception ex) block and replace it by a throws IOException on the method. All that code can throw is an IOException. You shouldn't swallow that exception, but let the container handle it. This also allows for a more centralized exception logging by a Filter.
You cannot forward (or redirect) once there's been content output to the client.
What you're trying to do would be equivalent to getting two responses back for a single request-you can get a file back, or a new page, but not both.

Resources