Streaming a pdf through a servlet to a browser - java

I have yer typical servlet that streams a pdf to a browser. The pdfs are stored on an internal server from which my servlet fetches. If I hit the servlet directly from the browser, the pdf is displayed. If I try the same URL in an <IMG> tag in a web page, ... broken pipe.
Any insight on why this should be?
As an experiment, I can stream gifs without issue.
Here's the code which I pretty much scavenged from the innerwebs:
public class PdfServlet extends HttpServlet {
private static final Logger log = Logger.getLogger(PdfServlet.class.getName());
#Override
public void doGet(HttpServletRequest req, HttpServletResponse res) {
String url = (String) req.getParameter("url");
log.info("The URL is " + url);
String format = "application/pdf";
// String format = "image/gif";
streamBinaryData(url, format, res);
}
/*
* This Method Handles streaming Binary data
* <p>
* #param String urlstr ex: http;//localhost/test.pdf etc.
* #param String format ex: pdf or audio_wav or msdocuments etc.
* #param ServletOutputStream outstr
* #param HttpServletResponse resp
*/
private void streamBinaryData(
String urlstr,
String format,
HttpServletResponse resp) {
ServletOutputStream outstr = null;
String ErrorStr = null;
try {
outstr = resp.getOutputStream();
//find the right MIME type and set it as contenttype
resp.setContentType(format);
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
URL url = new URL(urlstr);
URLConnection urlc = url.openConnection();
int length = urlc.getContentLength();
resp.setContentLength(length);
// resp.setHeader("Content-Length", String.valueOf(+length));
// resp.setHeader("Content-Disposition", "inline");
// Use Buffered Stream for reading/writing.
InputStream in = urlc.getInputStream();
bis = new BufferedInputStream(in);
bos = new BufferedOutputStream(outstr);
byte[] buff = new byte[length];
int bytesRead;
// Simple read/write loop.
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
log.info("Got a chunk of " + bytesRead);
bos.write(buff, 0, bytesRead);
}
} catch (Exception e) {
e.printStackTrace();
ErrorStr = "Error Streaming the Data";
outstr.print(ErrorStr);
} finally {
log.info("finally!!!");
if (bis != null) {
bis.close();
}
if (bos != null) {
bos.close();
}
if (outstr != null) {
outstr.flush();
outstr.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
...and the HTML file. The pdf fails with a broken pipe and the gif image is displayed even though the content type is being returned as 'application/pdf'.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Cheesy Servlet Experiment</title>
</head>
<body>
<h1>Cheesy Servlet Experiment</h1>
<P>
<img src="http://10.0.0.9/ServletExperiment/pdf?url=http%3a%2f%2fwww.samplepdf.com%2fsample.pdf" alt="yah mon">
<P>
<img src="http://10.0.0.9/ServletExperiment/pdf?url=http%3a%2f%2fbbs.homeshopmachinist.net%2fimages%2fstatusicon%2fforum_new.gif" alt="yah mon">
</body>
</html>
Edit - the following works in FF. I don't know how standard it is.
<object data="http://www.samplepdf.com/sample.pdf" type="application/pdf" width="600" height="600">
alt : test.pdf
</object>
Interesting info here. Looks reasonably well supported.

Can your browser even display PDF files in img elements? That's really unlikely. I think the browser ends the connection when it finds out that it's actually not an image.
Some browsers don't complain about content-types. They check the image file and figure out which format the image is in by itself. That can explain why your GIF image is displayed.

The problem is that you are trying to display a PDF document with the img Tag. img can handle only simple image formats like JPEG, GIF or PNG.
Usually the plain browser is not able to display PDF content on its own. If there is no PDF viewer plugin installed then the browser will only show a save dialog to download the PDF file.
So the safest way would be that your HTML page only contains a link to your PDF file. Maybe with a target="_blank" to open a new browser window.

you should use an anchor tag (if you have to use an img, have an A wrap the IMG with the IMG's src pointing to a real image). The href of the anchor will be your servelt that displays PDFs. make suer you set the right content-type in the servlet.

First thing: you are trying to read length at once.
this is not a good practice as it doent assure you of the task
try reading smaller chunks by
byte [] read_buffer = new byte[1024 * 10]
This way u read 10kb at once.
And write those 10kb on bos.
Keep this loop until you get -1 by the read function.
And yes. You shoud not use img tag for pdf

Related

Why doesn'n create pdf-documents in java servlet? [duplicate]

This question already has answers here:
How can I serve a PDF to a browser without storing a file on the server side?
(4 answers)
Closed 6 years ago.
I use iText/Pdfbox to create a PDF document. Everything works when I create the PDF using a standalone Java class like this:
public static void main(String[] args){
...
...
...
}
The document is created correctly.
But I need create a PDF document from a Servlet. I paste the code into the get or post method, run that servlet on the server, but the PDF document isn't created!
This code works as a standalone application:
This code doesn't work:
Please read the documentation. For instance the answer to the question How can I serve a PDF to a browser without storing a file on the server side?
You are currently creating a file on your file system. You aren't using the response object, meaning you aren't sending any bytes to the browser. This explains why nothing happens in the browser.
This is a simple example:
public class Hello extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("application/pdf");
try {
// step 1
Document document = new Document();
// step 2
PdfWriter.getInstance(document, response.getOutputStream());
// step 3
document.open();
// step 4
document.add(new Paragraph("Hello World"));
document.add(new Paragraph(new Date().toString()));
// step 5
document.close();
} catch (DocumentException de) {
throw new IOException(de.getMessage());
}
}
}
However, some browsers experience problems when you send bytes directly like this. It's safer to create the file in memory using a ByteArrayOutputStream and to tell the browser how many bytes it can expect in the content header:
public class PdfServlet extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
// Get the text that will be added to the PDF
String text = request.getParameter("text");
if (text == null || text.trim().length() == 0) {
text = "You didn't enter any text.";
}
// step 1
Document document = new Document();
// step 2
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter.getInstance(document, baos);
// step 3
document.open();
// step 4
document.add(new Paragraph(String.format(
"You have submitted the following text using the %s method:",
request.getMethod())));
document.add(new Paragraph(text));
// step 5
document.close();
// setting some response headers
response.setHeader("Expires", "0");
response.setHeader("Cache-Control",
"must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
// setting the content type
response.setContentType("application/pdf");
// the contentlength
response.setContentLength(baos.size());
// write ByteArrayOutputStream to the ServletOutputStream
OutputStream os = response.getOutputStream();
baos.writeTo(os);
os.flush();
os.close();
}
catch(DocumentException e) {
throw new IOException(e.getMessage());
}
}
}
For the full source code, see PdfServlet. You can try the code here: http://demo.itextsupport.com/book/
You wrote in a comment
This demo writes the PDF file into the browser. I want to save the PDF on my hard drive.
This question could be interpreted in two different ways:
You want to write the file to a specific directory on the user's disk drive without any user interaction. This is forbidden! It would be a serious security hazard if a server could force a file to be written anywhere on a user's disk drive.
You want to show a dialog box so that the user can save the PDF on his disk drive in a directory of his choice instead of just showing the PDF in the browser. In this case, please take a closer look at the documentation. You'll see this line: response.setHeader("Content-disposition","attachment;filename="+ "testPDF.pdf"); You can set the Content-disposition to inline if you want the PDF to open in the browser, but in the question, the Content-disposition is set to attachment which triggers a dialog box to open.
See also How to show a Save As dialog for a iText generated PDF?

How to generate and download an Excel report using jsp servlet [duplicate]

This question already has answers here:
Implementing a simple file download servlet [duplicate]
(5 answers)
Closed 7 years ago.
I have generated the Excel report using Apache POI. What I want now, I want to send this to the browser for download. My JSP is as follows.
<html>
<head><title> Excel Generator</title>
</head>
<body>
generate report
</body>
</html>
Here is my servlet code
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String report_path=request.getSession().getServletContext().getInitParameter("REPORT_PATH");
HouseHoldReportGenerator report = new HouseHoldReportGenerator("HOUSE_HOLD",attrStr_,dbParam);
report.Write_Report___(report_path, dbParam);
System.out.println("path-->"+report_path+report.getFileName_());}
I have my report generation java class as HouseHoldReportGenerator. It generates the report. But What I want from one click on the link in jsp page I want it to be generated and downloaded. I can get the report destination also.
you should add the following in your servlet method..
try{
//This is for downloading
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=nameOfExcel");
File file = new File("path_to_the_file/excelfile.xls"); //<- the name of excel that you have already created.
FileInputStream fileIn = new FileInputStream(file);
ServletOutputStream out = response.getOutputStream();
byte[] outputByte = new byte[4096];
//copy binary contect to output stream
while(fileIn.read(outputByte, 0, 4096) != -1)
{
out.write(outputByte, 0, 4096);
}
fileIn.close();
out.flush();
out.close();
}
catch(IOException e){
e.printStackTrace();
}
Look at that answer: https://stackoverflow.com/a/14281064/5594550
Basically you need to set a correct HTTP header and then stream the file to the client through response.getOutputStream()

Displaying Pdf Document by Servlet

I am trying to show pdf document in a iframe. I have set the source of the iframe to a servlet and passing some parameter to the servlet.
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String docName = request.getParameter("docName");
String id = request.getParameter("id");
if (StringUtils.isNotBlank(id) && StringUtils.isNotBlank(docName)) {
DocumentService service = DamServiceProvider.PROVIDER.getDocumentService();
FileInBean fileInBean = new FileInBean();
fileInBean.setDocName(docName);
fileInBean.setId(Integer.valueOf(id));
FileDataBean fileDataBean = service.getFileDataBean(fileInBean);
if (fileDataBean.getStatusCode() == 0) {
Map<String, String> headerFieldMap = fileDataBean.getHeaderFieldMap();
String contentType = headerFieldMap.get("Content-type");
String contentLength = headerFieldMap.get("Content-Length");
String contentDisposition = headerFieldMap.get("Content-Disposition");
byte[] stream = fileDataBean.getStream();
ByteArrayInputStream inputStream = new ByteArrayInputStream(stream);
OutputStream outputStream = response.getOutputStream();
response.reset();
response.setBufferSize(4096);
response.setContentLength(Integer.valueOf(contentLength));
response.setContentType(contentType);
response.setHeader("Content-Disposition", contentDisposition);
System.out.println(contentDisposition);
IOUtils.copy(inputStream, outputStream);
outputStream.close();
inputStream.close();
}
}
} catch (Exception ex) {
Log.error(this, ex.getMessage());
}
}
Now in my page I have a master–detail interface. The master part contains a carousel of series of pdf file items. On clicking the item I am refreshing the detail view which contains the iframe.
I can see the servlet get called. Most of the times the iframe is displaying the pdf document. But sometimes it is showing weird xml structure which contains xml tags and some unreadable output. Please see the attach image:
This is not happening for a particular file. If a file shows this output, sometime later if click the item it shows the valid pdf and if an item shows a valid pdf sometime later it shows this kind of output if I click on it. When the iframe shows this type of output my browser displays an information that this pdf document might be corrupted.
I have checked the repository where the files are and I have found no issues there. All of them are valid pdf and I can download and open them by pdf reader.
I am unable to find the cause of this issue. Any pointer would be very helpful.
Update - 1
I have checked the output. It ends with %%EOF and has %PDF in the beginning.
Update - 2
I have checked in Chrome's Network Console the GET is returning mainly three types of content-type: application/pdf, text/plain, application/octet-stream.
application/pdf: it is showing the pdf.
text/plain it is showing the content that I mentioned above.
application/octet-stream didn't arise in Firefox but in Chrome and in that case it is opening the download file window.
I have placed a log in the servlet to see the content-type that returned from service. For all the cases it is application/pdf.
I think it maybe a problem with the content-Type, you can confirm if this is the espected in your browser with the developer tools (in the network console for Chrome).
try something like this.
File pdfFile = new File(this.pdfStoreLocation + pdfFileName);
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" + pdfFileName);
response.setContentLength((int) pdfFile.length());
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(pdfFile));
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
// byte array declared
byte[] buf = new byte[2048];
boolean eof = false;
while (!eof) {
int length = bis.read(buf);
if (length == -1) {
eof = true;
}else {
bos.write(buf, 0, length);
}
}
try {
bis.close();
}catch (IOException ex) {
LOGGER.error("Exception in closing buffered input stream on pdf file->" + this.pdfStoreLocation + pdfFileName);
}
try {
bos.flush();
}catch (IOException ex) {
LOGGER.error("Exception in fliushing buffered output stream on pdf file->"
+ this.pdfStoreLocation + pdfFileName);
}
bos.close();

Display PDF in browser with a servlet

I want to display a PDF file in browser. I have the path to the pdf in JS and I am making a call to grab the PDF as a servlet from java. Here's what I have so far:
JavaScript:
RequestManager.getJSON(Config.server + "getPDF.json?pdfPath=" + this.pathToPdfFile, (function(data){
$("#" + this.divId).append('<object id="' + this.pdfObjectId + '" data="' + data + '" type="application/pdf" width="600" height="800"></object>');
ResizeManager.addResizeHandler(this.pdfObjectId, this.divId, -10, -10);
}).bind(this));
Java:
#RequestMapping("/getPDF")
public void pdfPathToServlet(Model model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String pdfPath = request.getParameter("pdfPath");
if (pdfPath == null || pdfPath.equals(""))
throw new ServletException("Invalid or non-existent file parameter in UrlServlet servlet.");
if (pdfPath.indexOf(".pdf") == -1)
pdfPath += ".pdf";
File pdf = new File(pdfPath);
String pdfName = pdfPath.substring(pdfPath.lastIndexOf("/") + 1, pdfPath.length());
logger.debug(pdfName);
ServletOutputStream stream = null;
BufferedInputStream buf = null;
try
{
stream = response.getOutputStream();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "inline; filename='" + pdfName + "'");
FileInputStream input = new FileInputStream(pdf);
response.setContentLength((int) pdf.length());
buf = new BufferedInputStream(input);
int readBytes = 0;
while ((readBytes = buf.read()) != -1)
stream.write(readBytes);
}
catch (IOException ioe)
{
throw new ServletException(ioe.getMessage());
}
finally
{
if (stream != null)
stream.close();
if (buf != null)
buf.close();
}
}
My problem is that this is showing the binary output in my browser as text.
I'm not sure what I am doing incorrectly. I have tried changing the header to be attachment instead of inline, but that showed the same thing. I believe I want inline though, as I wish to show it in browser and not download it.
Your JavaScript part makes no sense. You're obtaining a PDF file as ajax response and then attempting to set it as data attribute of the <object> element. The data attribute must point to a real URL, not to the file content. Fix your JS accordingly:
$("#" + this.divId).append('<object id="' + this.pdfObjectId + '" data="' + Config.server + "getPDF.json?pdfPath=" + this.pathToPdfFile + '" type="application/pdf" width="600" height="800"></object>');
The webbrowser will take care about sending the appropriate HTTP request on the given URL and initializing/rendering the <object> element using the Adobe Acrobat Reader plugin — if any available, I'd rather enclose a PDF inside the <object> so that there's at least a graceful degradation to a download link.
Unrelated to the concrete question, that Java code is not part of a servlet at all, but a Spring MVC action. I recommend to get your terms straight and read in our Servlets wiki page to learn what they really are.
response.setHeader("Content-Disposition", "attachment;filename=" + pdfName);
response.setHeader("Content-Disposition", "inline; filename='" + pdfName + "'");
You cannot display a PDF inline. It needs to be alone on its own page (or Iframe).

iText generated PDF not shown correctly in Chrome

I am using the iText library in Java to generate a pdf file. The idea is that a user fills in some information and that when the user clicks on the generate button the pdf is shown in a new tab in the browser. Now I have stumbled upon some problems doing this, which are :
- the URL does not change, so instead of /application/user.pdf I get /application/dashboard.xhtml
- I can save the pdf file in all browsers except for Chrome.
Please note that I don't want to save it on disc but simply show the pdf in the browser so the user can choose if he wants to save it.
Here is the code that I use to generate my pdf :
public static void createPdf(User user, byte languageNumber, HttpServletResponse response) {
Document document = new Document();
try {
/* PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream("c://" + user.getUsername() + "_" + languageCode + ".pdf"));*/
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter.getInstance(document, baos);
document.addTitle("Your CV");
document.addSubject("This is your CV");
document.addKeywords("CV");
document.addAuthor(user.getUsername());
document.open();
document.add(
new Paragraph(user.getPersonalInformation().getFirstname() + " " + user.getPersonalInformation().getLastname()));
document.close();
// setting some response headers
response.setHeader("Expires", "0");
response.setHeader("Cache-Control",
"must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
// setting the content type
response.setContentType("application/pdf");
response.setContentLength(baos.size());
//ServletOutputStream out = response.getOutputStream();
OutputStream out = response.getOutputStream();
baos.writeTo(out);
out.flush();
out.close();
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
*This method is behind a button on my JSF page *
public String exportPdf() {
user = userService.retrieveLoginUser();
FacesContext context = FacesContext.getCurrentInstance();
try {
Object response = context.getExternalContext().getResponse();
if (response instanceof HttpServletResponse) {
HttpServletResponse hsr = (HttpServletResponse) response;
PdfCreator.createPdf(user, selectLanguage, hsr);
//Tell JSF to skip the remaining phases of the lifecycle
context.responseComplete();
}
return "../" + user.getUsername() + ".pdf";
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Used technologies :
- JSF 2.0
- Facelets
- iText
Thanks in advance :D
The way that I have achieved this in the past is by creating a seperate Servlet to serve PDF documents directly. In the web.xml file you would specify the servlet mapping to *.pdf.
What you can do then is rather than override the FacesServlet response bytes to server the PDF file you just redirect the response to filename.pdf, passing needed parameters in the URL.
Your PDF servlet can actually do the work of building the necessary PDF, it will open in a seperate tab and the URL will match the response redirect.
Does chrome open the PDF and then not render it correctly? In that case, please open an issue at http://new.crbug.com and attach an example PDF file that shows the problem. Reply with the issue number here.

Categories

Resources