IText add new page to existing pdf with PDF Reader - jsf

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;
}

Related

Returning excel file using spring boot controller

I was trying to make a rest endpoint in Spring Boot which reads from DB, generates an excel file(Using Apache POI) which is returned to the user using HttpServletResponse but when I invoke this, the excel is getting created but it's not downloading. I had some other code earlier in place which was working fine but I accidentally removed that and now I'm stuck. Any help/leads are appreciated.
#RequestMapping(path = "/save", method = RequestMethod.GET)
public ResponseEntity<String> saveToXls(#RequestParam String id, #RequestParam String appName, HttpServletResponse response) {
AppInstance appInstance = appInstanceRepo.get(id);
List<DownloadDetail> downloadDetailList = downloadDAO.searchByInstanceId(id);
//List<DownloadDetail> downloadDetailList = appInstance.getDownloads();
System.out.print("LIST SIZE:" + downloadDetailList.size());
String fileName = appName + " report";
File myFile = new File(fileName + ".xls");
FileOutputStream fileOut;
downloadDetailList.forEach(downloadDetail -> System.out.print(downloadDetail.getSid()));
try {
try (HSSFWorkbook workbook = new HSSFWorkbook()) {
HSSFSheet sheet = workbook.createSheet("lawix10");
HSSFRow rowhead = sheet.createRow((short) 0);
rowhead.createCell((short) 0).setCellValue("SID");
rowhead.createCell((short) 1).setCellValue("Download Time");
rowhead.createCell((short) 2).setCellValue("OS Version");
int i = 0;
for (DownloadDetail downloadDetail : downloadDetailList) {
System.out.print("In loop -2");
HSSFRow row = sheet.createRow((short) i);
row.createCell((short) 0).setCellValue(downloadDetail.getSid());
row.createCell((short) 1).setCellValue(downloadDetail.getDownloadTime());
row.createCell((short) 2).setCellValue(downloadDetail.getOsVersion());
i++;
}
fileOut = new FileOutputStream(myFile);
workbook.write(fileOut);
}
fileOut.close();
byte[] buffer = new byte[10240];
response.addHeader("Content-disposition", "attachment; filename=test.xls");
response.setContentType("application/vnd.ms-excel");
try (
InputStream input = new FileInputStream(myFile);
OutputStream output = response.getOutputStream();
) {
for (int length = 0; (length = input.read(buffer)) > 0;) {
output.write(buffer, 0, length);
}
}
response.flushBuffer();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
EDIT:
I tried to do it another way as shown below:
try (InputStream is = new FileInputStream(myFile)) {
response.addHeader("Content-disposition", "attachment; filename=test.xls");
response.setContentType("application/vnd.ms-excel");
IOUtils.copy(is, response.getOutputStream());
}
response.flushBuffer();
This also doesn't seem to cut it.
This is a my example. Probably the issue is how you manage the OutputStream:
ServletOutputStream os = response.getOutputStream();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=\""+fileName+".xls\"");
workbook = excelStrategyMap.get(strategy).export(idList, status, params);
workbook.write(os);
workbook.close();
os.flush();
response.flushBuffer();
Once you get the workbook file, set the file name and file type. and add the response header and content type as mentioned below.
Then write the file to the response and flush it's buffer.
XSSFWorkbook file = excelUploadService.downloadDocument();
String filename = "Export.xlsx";
String filetype = "xlsx";
response.addHeader("Content-disposition", "attachment;filename=" + filename);
response.setContentType(filetype);
// Copy the stream to the response's output stream.
file.write(response.getOutputStream());
response.flushBuffer();
In the client side, get the response from the REST API and set the content type received by the response object. Using FileSaver library save the file into your local file system.
Here is the documentation for FileSaver js -- File saver JS Library
var type = response.headers("Content-Type");
var blob = new Blob([response.data], {type: type});
saveAs(blob, 'Export Data'+ '.xlsx');
#GetMapping(value = "/", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
#ResponseBody
public byte[] generateExcel() {
byte[] res = statisticsService.generateExcel();
return res;

spring mvc export excel failed sometimes and data displays garblend on the page

There is a data report as the following in my project, I want to export it as excel:
But the wierd thing is that sometimes it exports successfully and sometimes failed. I have tried many times and it turns out that when the excel sheet is over 17 lines, it will lead to this:
the page redirects to a new page with garblend data.
the post request has been changed to get request.
It's sure that the excel is successfully created even if it exports failed, because I have written it to disk and checked the file. The following is the controller, what goes wrong with me?
#RequestMapping("/download")
public void download(HttpServletRequest request, HttpServletResponse response, ReportCondition condition){
try {
List<HashMap<String, String>> mapList = reportFormService.find(condition);
if(mapList == null || mapList.size() == 0){
logger.info("No reports...");
}
Map<String, Date> dateMap = DateConditionUtil.getStartEndDate1(condition.getStartDate(), condition.getEndDate());
String startDate = DateFormatUtils.format(dateMap.get("startDate"), "yyyyMMdd");
String endDate = DateFormatUtils.format(dateMap.get("endDate"), "yyyyMMdd");
String[] titleArr = new String[]{"序号","日期","应用系统","短信服务商","请求发送总数","请求成功数量","实际短信条数","实际成功条数","费用","失败数量","成功率"};
String[] fieldArr = new String[]{"ID","RECORDDATE","APPNAME","PROVIDERNAME","SENDCOUNT","SUCCESSCOUNT","SENDSUM","SUCCESSSUM","TOTALFEE","FAILURECOUNT","SUCCESSRATE"};
ByteArrayOutputStream os = new ByteArrayOutputStream();
WorkBookUtil.createExcel(titleArr, fieldArr, mapList, os);
try(BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(os.toByteArray()));
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())){
byte[] buff = new byte[1024];
int bytes;
while (-1 != (bytes = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytes);
}
bos.flush();
}catch (Exception e){
logger.error(e);
}
response.setCharacterEncoding("utf-8");
response.setHeader("content-disposition", "attachment;filename=" + String.format("%s-%s.xls", startDate, endDate));
response.setContentType("application/vnd.ms-excel;charset=utf-8");
} catch (IOException e){
logger.error("Export failed", e);
}
}
You should set the response headers first before you create the .xls file. So try this:
response.setCharacterEncoding("utf-8");
response.setHeader("content-disposition", "attachment;filename=" + String.format("%s-%s.xls", startDate, endDate));
response.setContentType("application/vnd.ms-excel;charset=utf-8");
ByteArrayOutputStream os = new ByteArrayOutputStream();
WorkBookUtil.createExcel(titleArr, fieldArr, mapList, os);
// ...
Try adding your response headers before writing the bytes out to the user. It would help to add a content-length header as well. You can get that after your call to createExcel with os.toByteArray().length. In addition close() your bos after you flush() it.

why downloading to file is not working in jsf? [duplicate]

This question already has answers here:
How to provide a file download from a JSF backing bean?
(5 answers)
Closed 5 years ago.
i made a call to download() method to save json into xml with extension ".svg". The jsondata is global variable store json.
public void download(){
File file = exportFile(jsondata);
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
writeOutContent(response, file, file.getName());
FacesContext.getCurrentInstance().responseComplete();
FacesContext.getCurrentInstance().renderResponse();
}
and the exportFile(jsondata) is
public File exportFile(String jsonData){
File xmlFile = null;
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
JSONObject jsonObject = new JSONObject(jsonData);
Element root = doc.createElement("web");
doc.appendChild(root);
Element rootElement1 = doc.createElement("class");
rootElement1.appendChild(doc.createTextNode(jsonObject.getString("class")));
root.appendChild(rootElement1);
JSONArray jsonArray1 = (JSONArray) jsonObject.get("nodes");
Element rootElement2 = doc.createElement("nodes");
root.appendChild(rootElement2);
for (int i = 0; i < jsonArray1.length(); i++) {
Element staff = doc.createElement("node");
rootElement2.appendChild(staff);
JSONObject childObject = (JSONObject) jsonArray1.get(i);
Iterator<String> keyItr = childObject.keys();
while (keyItr.hasNext()) {
String key = keyItr.next();
Element property = doc.createElement(key);
property.appendChild(doc.createTextNode(childObject.getString(key)));
staff.appendChild(property);
}
}
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
//for pretty print
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(doc);
xmlFile = new File("file.svg");
//write to console or file
// StreamResult console = new StreamResult(System.out);
StreamResult file = new StreamResult(xmlFile);
//write data
// transformer.transform(source, console);
transformer.transform(source, file);
} catch (Exception pce) {
pce.printStackTrace();
}
return xmlFile;
}
finally to write this one file writeOutContent()
public void writeOutContent(final HttpServletResponse res, final File content, final String theFilename) {
if (content == null) {
System.out.println("content is null");
return;
}
try {
res.setHeader("Content-Disposition", "attachment; filename=\"" + theFilename + "\"");
System.out.println("res " + res.getHeader("attachment; filename=\"" + theFilename + "\""));
res.setContentType("application/octet-stream");
FileInputStream fis = new FileInputStream(content);
OutputStream os = res.getOutputStream();
int bt = fis.read();
while (bt != -1) {
os.write(bt);
bt = fis.read();
}
os.flush();
fis.close();
os.close();
} catch (Exception ex) {
Logger.getLogger(DownloadFile.class.getName()).log(Level.SEVERE, null, ex);
}
}
i can see the xml in console but what am doing wrong that its not downloading?? please help me.
thanks in advance.
i got the mistake. it was not in above code. if we make through commandLink then it won't work but if make call through commandButton then it worked. if you want know know more read difference between commandButton vs commandLink

Best way to cancel creating a outputsteam that was to create a new pdf file?

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.

Blank pages when creating and downloading a PDF file (iText & JSF)

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();
}

Resources