I'm having a problem when I try to create a PDF file using iText and want to download it immediately afterwards. First I create a PDF file using the iText library, the file is written to a TEMP folder on the server, this all works fine. But afterwards I call a download screen for downloading the PDF file from the TEMP folder to the client, and here something goes wrong. The download screen shows a Firefox (browser) icon instead of the Acrobat icon. When I donwload the file I only get to see blank PDF pages, but the number of pages is correct. E.g. I have a PDF file of 4 pages, I get 4 blank pages as a result, there is no content. The PDF file in the TEMP folder however is correct, it has got 4 pages with the correct content.
This is my java code, it is executed when the user clicks a h:commandLink
public <E> String createPDF(E type, boolean print) throws Exception {
Document document = new Document();
// create a File name for the document
getPdfNaam(type);
try {
//create a PDF writer
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(TEMP + naam + ".pdf"));
//open the PDF document
document.open();
} catch (Exception e) {
e.printStackTrace();
}
//build the PDF file using iText
buildPDFContent(document, type);
//close the PDF document
close(document);
String downloadFile = TEMP + naam + ".pdf";
//call Servlet for download screen in the browser
ServletContext context = (ServletContext) ContextProvider.getFacesContext().getExternalContext().getContext();
HttpServletResponse response = (HttpServletResponse) ContextProvider.getFacesContext().getExternalContext().getResponse();
response.setContentType("application/force-download");
downloadFile = TEMP + naam + ".pdf";
byte[] buf = new byte[1024];
try {
File file = new File(downloadFile);
long length = file.length();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
ServletOutputStream out = response.getOutputStream();
response.setContentLength((int) length);
while ((in != null) && ((length = in.read(buf)) != -1)) {
out.write(buf, 0, (int) length);
}
in.close();
out.close();
} catch (Exception exc) {
exc.printStackTrace();
}
response.addHeader("Content-Disposition", "attachment; filename=\"" + naam + ".pdf" + "\"");
return null;
}
I found the code for calling a download screen on this website http://www.winstonprakash.com/articles/jsf/file_download_link.htm
I searched on Google and Stack Overflow, but I couldn't find any related questions. I'm using JSF 2.0 Any help would be greatly appreciated!
The content type should be set to application/pdf and the content disposition header should be set before any byte is been written to the response, otherwise it's too late to set it. Plus, you can also just write the PDF to the outputstream of the response immediately.
All with all, the method can be simplified as follows:
public <E> String createPDF(E type, boolean print) throws Exception {
getPdfNaam(type); // ??? It should *return* name, not change/set the local value.
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.setResponseHeader("Content-Type", "application/pdf");
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + naam + ".pdf" + "\"");
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, ec.getResponseOutputStream());
document.open();
buildPDFContent(document, type);
close(document);
}
Also ensure that you're calling FacesContext#responseComplete() to signal JSF that you've already taken the response handling in your hands so that it knows that it doesn't need to navigate to some view.
FacesContext.getCurrentInstance().responseComplete();
you can you the outputstream to response immediately. The below is my code:
OutputStream out = response.getOutputStream();
response.setContentType("application/x-msdownload;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;"+"filename="+System.currentTimeMillis()+".pdf");
Document document = new Document(PageSize.A4, 10, 10, 10,10);
PdfWriter.getInstance(document, out);
document.open();
//The below is document add data
//....
//close flow
if(document!=null){
document.close();
}
if(out!=null){
out.flush();
out.close();
}
Related
I am working with JSF and I want to open a PDF file in a new tab when I click on a button.
XHTML
<p:commandButton onclick="this.form.target = '_blank'"
actionListener="#{managedBean.openFile(file)}"
ajax="false" />
Managed bean
public void openFile( File file ) {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
BufferedInputStream input = null;
BufferedOutputStream output = null;
try {
// Open file.
input = new BufferedInputStream(new FileInputStream(file), 10240);
// Init servlet response.
response.reset();
// lire un fichier pdf
response.setHeader("Content-type", "application/pdf");
response.setContentLength((int)file.length());
response.setHeader("Content-disposition", "attachment; filename=" + node.getNomRepertoire());
response.setHeader("pragma", "public");
output = new BufferedOutputStream(response.getOutputStream(), 10240);
// Write file contents to response.
byte[] buffer = new byte[10240];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
// Finalize task.
output.flush();
} finally {
// Gently close streams.
output.close();
input.close();
}
}
The problem is when I use this method it only downloads the file. For your information, I'm following this post: http://balusc.omnifaces.org/2006/05/pdf-handling.html
You should change
response.setHeader("Content-disposition", "attachment; filename=" + node.getNomRepertoire());
into
response.setHeader("Content-disposition", "inline; filename=" + node.getNomRepertoire());
So, use inline instead of attachment.
See also:
How to force files to open in browser instead of download (pdf)?
I have SSJS in a button that opens a pdf file, writes to some fields on it (Acroform) and then downloads the file to the user. All works great (using pdfbox) but I wanted to be a good programmer and if the original pdf file was not available then cancel the operation. Otherwise, the user still gets prompted to open the file but Adobe Reader reports the file is corrupted (obviously it will be). I do my pdf operations in a Java class that I call and pass in the outputStream of the response object. Below is my SSJS. If I test the ret value from newVal.outputPdf and put all the other code in the if statement then my XPage is just blank. I assume because the response and outputStream was already opened?
Howard
importPackage(com.tlcc);
var newVal = new PdfBoxTest();
importPackage(java.net);
importPackage(java.lang);
var con = facesContext.getExternalContext();
var response:com.ibm.xsp.webapp.XspHttpServletResponse = con.getResponse();
try {
var writer:javax.servlet.ServletOutputStream = response.getOutputStream();
//get the stream
var ret = newVal.outputPdf(writer, "http://localhost/pdfexportcc.nsf/certificate.pdf");
// setting response headers for browser
print("Good output");
response.setContentType("application/pdf");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setHeader( "Content-Disposition", "attachment; filename=\"mypdf.pdf\"" );
writer.flush();
writer.close();
print("in close");
facesContext.responseComplete();
} catch (e) {
var errorMessage = "An error has occured: " + e.toString();
_dump(errorMessage);
writer.close();
response.sendError(500, errorMessage);
}
Tried again with all the work being done in Java. I called this method from a button. Works fine with a valid url but when the url is bad it throws an error. Exception Can't get a Writer while an OutputStream is already in use.
public boolean outputAllInJavaPdf() {
try {
FacesContext context = FacesContext.getCurrentInstance();
XspHttpServletResponse response = (XspHttpServletResponse) context.getExternalContext().getResponse();
ServletOutputStream writer = response.getOutputStream();
InputStream docUrl = new URL("http://localhost/pdfexportcc.nsf/certifxxicate.pdf").openStream();
pdfDoc = PDDocument.load(docUrl);
System.out.println("Number of pages is " + pdfDoc.getNumberOfPages());
setField("Student", "James Namce");
setField("CourseName", "XPages Development 2 for Notes and Domino 9");
setField("Instructor", "John Smith");
System.out.println("After set field");
pdfDoc.save(writer);
pdfDoc.close();
response.setContentType("application/pdf");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setHeader("Content-Disposition", "attachment; filename=\"mypdf.pdf\"");
writer.flush();
writer.close();
context.responseComplete();
return true;
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
return false;
}
}
All depends on what you tell the browser.
You use content type of PDF file. Browser opens (downloads) PDF file. Anything you put inside, for example error page, is treated as content of PDF file.
So in case PDF generation fails, do not set that content type and redirect browser to error page, or back to original XPage with explanation.
public boolean outputAllInJavaPdf() {
try {
FacesContext context = FacesContext.getCurrentInstance();
XspHttpServletResponse response = (XspHttpServletResponse) context.getExternalContext().getResponse();
ServletOutputStream writer = response.getOutputStream();
boolean servePdf = true;
try {
InputStream docUrl = new URL("http://localhost/pdfexportcc.nsf/certifxxicate.pdf").openStream();
pdfDoc = PDDocument.load(docUrl);
// do something to validate PDF
} catch (Exception e) {
//no PDF
servePdf = false;
}
if (servePdf) {
System.out.println("Number of pages is " + pdfDoc.getNumberOfPages());
setField("Student", "James Namce");
setField("CourseName", "XPages Development 2 for Notes and Domino 9");
setField("Instructor", "John Smith");
System.out.println("After set field");
pdfDoc.save(writer);
pdfDoc.close();
response.setContentType("application/pdf");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setHeader("Content-Disposition", "attachment; filename=\"mypdf.pdf\"");
} else {
// take care of no PDF response - redirect?
}
writer.flush();
writer.close();
context.responseComplete();
return true;
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
return false;
}
}
It is probably trying to render your Xpage, but you want to control the response yourself. To prevent rendering of the Xpage and take control of the writer, add rendered="false"to your Xpage.
What I am trying to do is add a new page to the end of the current pdf that I am editing right after setFields() method. I am getting it with PdfReader and never have a Document object so I am not sure how I would do this. Here are the two methods that are creating the PDF.
Edit :
Upon further investigation I found that you can insert a page with stamper.insertPage. But This seems to be a little too hard to do what I want with it. Is there a way to just merge two pdf's with acrofields together? And by merge I mean the first pdf have 2 pages and the second pdf have 1 page and put them together and now the pdf has 3 pages.
public static void createPdf(MyDocument document) {
try {
PdfReader pdfTemplate = initPdfReader();
if (pdfTemplate != null) {
OutputStream output = FacesContext.getCurrentInstance().getExternalContext().getResponseOutputStream();
PdfStamper stamper = new PdfStamper(pdfTemplate, output);
stamper.setFormFlattening(true);
AcroFields fields = stamper.getAcroFields();
setFields(document, fields, stamper);
stamper.close();
FacesContext.getCurrentInstance().responseComplete();
}
} catch (Exception e) {
BeanUtils.addMessage(new Message("An error has occurred while crating PDF.:" + ExceptionUtils.getStackTrace(e)));
LOG.error(e);
}
}
InitPdfReader()
public static PdfReader initPdfReader() throws Exception {
PdfReader pdfReader = null;
InputStream inStream = null;
try {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.responseReset();
ec.setResponseContentType("pdf");
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + "FileName" + "\"");
inStream = FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream("/resources/documents/MyDocument.pdf");
pdfReader = new PdfReader(inStream);
pdfReader.close();
} finally {
if (inStream != null) {
inStream.close();
}
}
return pdfReader;
}
I am having a folder, I am trying to zip it and than on a button click event it should be downloaded on the user's machine. I am able to generate the zip file correctly. I have written the code to download it also but after it is getting downloaded to the user's machine from the server. It shows unable to open zip file as it is invalid.
How is this caused and how can I solve it? Here's the code which performs the download functionality.
public String getAsZip() {
try {
FacesContext ctx = FacesContext.getCurrentInstance();
ExternalContext etx = ctx.getExternalContext();
HttpServletResponse response = (HttpServletResponse) etx
.getResponse();
ServletOutputStream zipFileOutputStream = response
.getOutputStream();
response.setContentType("application/octet-stream");
response.setHeader(
"Content-Disposition",
"attachment; filename=" + downloadLink.substring(downloadLink.lastIndexOf("\\") + 1,downloadLink.length()));
response.setHeader("Cache-Control", "no-cache");
File zipFile = new File(downloadLink);
FileInputStream stream = new FileInputStream(zipFile);
response.setContentLength(stream.available());
int length = 0;
byte[] bbuf = new byte[response.getBufferSize()];
BufferedInputStream in = new BufferedInputStream(stream);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((length = in.read(bbuf)) > 0) {
baos.write(bbuf, 0, length);
}
zipFileOutputStream.write(baos.toByteArray());
zipFileOutputStream.flush();
zipFileOutputStream.close();
response.flushBuffer();
in.close();
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
return "successZip";
}
See JSF 2.0 RenderResponse and ResponseComplete
The problem is that you do not call FacesContext#responseComplete(). That's why JSF will still render the view after you attached the download and append that to the response. This will cause the zipfile to be broken.
On button click event or on Link button click, I want to download document from sharepoint document library and save it to the user's local disk.
Plz help me on this,If you have any code sample then please share
The problem with outputting a direct link to the file, is that for some content types it may just open in the browser window. If that is not the desired outcome, and you want to force the save file dialog, you'll need to write an ASP/PHP page that you could pass a filename to via the querystring. This page could then read the file and set some headers on the response to indicate that the content-disposition is and attachment.
For ASP.net, if you create a simple aspx page called download.aspx, add the following code into it, then put this file on a server somewhere you can download files by calling this page like this:
http://yourserveraddress/download.aspx?path=http://yoursharepointserver/pathtodoclibrary/file.ext
protected void Page_Load(object sender, EventArgs e)
{
string path = "";
string fileName = "";
path = Request.QueryString["path"];
if (path != null && path.Length > 0)
{
int lastIndex = path.LastIndexOf("/");
fileName = path.Substring(lastIndex + 1, (path.Length - lastIndex - 1));
byte[] data;
data = GetDataFromURL(path);
Response.Clear();
Response.AppendHeader("Content-Disposition", "attachment; filename=" + fileName);
Response.BinaryWrite(data);
Response.Flush();
}
}
protected byte[] GetDataFromURL(string url)
{
WebRequest request = WebRequest.Create(url);
byte[] result;
byte[] buffer = new byte[4096];
//uncomment this line if you need to be authenticated to get to the files on SP
//request.Credentials = new NetworkCredential("username", "password", "domain");
using (WebResponse response = request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (MemoryStream ms = new MemoryStream())
{
int count = 0;
do
{
count = responseStream.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, count);
} while (count != 0);
result = ms.ToArray();
}
}
}
return result;
}
I'd create a LinkButton and set the URL to the document's url programmatically.