[ Team LiB ] Previous Section Next Section

Recipe 13.1 Sending a PDF File


You want to send binary data representing a PDF file to a user.


Use a servlet and a java.io.BufferedInputStream to read the bytes from the file. Send the file content to the user with a javax.servlet.ServletOutputStream retrieved from the javax.servlet.http.HttpServletResponse object.


PDF files are ubiquitous on the Web, as most users are equipped with the Adobe Reader application that reads them. Example 13-1 takes a filename from a query string that is part of the request to the servlet, and responds with binary data that represents the PDF file. The servlet identifies the file that it sends to the client as the MIME type application/pdf by using the Content-Type response header.

Multipurpose Internet Mail Extensions (MIME) designate the media types of various data that are sent as email or as part of HTTP responses on the Web. A MIME type has a top-level type and a sub-type separated by a forward-slash character, as in text/html, application/pdf, or audio/mpeg. The Content-Type response header is the method the server response uses to convey the intended type and format of the data that the server sends to a network client such as a web browser. See the following Request For Comments (RFC) technical documents on MIME for more information: ftp://ftp.rfc-editor.org/in-notes/rfc2045.txt and ftp://ftp.rfc-editor.org/in-notes/rfc2046.txt.

Table 13-1 shows several MIME types that web developers may encounter.

Table 13-1. Some common MIME types


MIME type








Plaintext file






Graphics Interchange Format (GIF) image



JPEG image



PNG image



MP3 music file



Shockwave Flash animation

application/futuresplash or application/x-shockwave-flash


Microsoft Word document



Excel worksheet



PowerPoint document



The request to the servlet looks like this:


Example 13-1 checks to see if the request parameter file is valid and then adds the file extension .pdf to the filename if it does not already have that suffix. This is the filename the HTTP response recommends to the browser (it will appear as the default filename in any displayed "Save As" windows).

Jason Hunter in Java Enterprise Best Practices (O'Reilly) points out that it is often useful to include the intended filename (for the "Save As" dialog box the browser produces) directly in the URL as extra path info. The browser detects that name as the requested resource and may specify the name in the "Save As" dialog window. An example URL in this case looks like:


Example 13-1. Sending a PDF file as binary data
package com.jspservletcookbook;           

import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;

import javax.servlet.*;
import javax.servlet.http.*;

public class SendPdf extends HttpServlet {

  public void doGet(HttpServletRequest request, 
    HttpServletResponse response) throws ServletException, 
      IOException {
      //get the filename from the "file" parameter
      String fileName = (String) request.getParameter("file");
      if (fileName == null || fileName.equals(""))
           throw new ServletException(
            "Invalid or non-existent file parameter in SendPdf servlet.");
      // add the .pdf suffix if it doesn't already exist
      if (fileName.indexOf(".pdf") == -1)
          fileName = fileName + ".pdf";
      //where are PDFs kept?
     String pdfDir = getServletContext( ).getInitParameter("pdf-dir");
     if (pdfDir == null || pdfDir.equals(""))
           throw new ServletException(
             "Invalid or non-existent 'pdfDir' context-param.");
     ServletOutputStream stream = null;
     BufferedInputStream buf = null;
     stream = response.getOutputStream( );
     File pdf = new File(pdfDir + "/" + fileName);
     //set response headers
        "Content-Disposition","attachment; filename="+fileName );

     response.setContentLength( (int) pdf.length( ) );
     FileInputStream input = new FileInputStream(pdf);
     buf = new BufferedInputStream(input);
     int readBytes = 0;

     //read from the file; write to the ServletOutputStream
     while((readBytes = buf.read( )) != -1)

     } catch (IOException ioe){
        throw new ServletException(ioe.getMessage( ));
     } finally {
     //close the input/output streams
         if (stream != null)
             stream.close( );
          if (buf != null)
          buf.close( );
    } //end doGet
  public void doPost(HttpServletRequest request, 
    HttpServletResponse response)
      throws ServletException, IOException {

Example 13-1 gets the directory where the PDF files are stored from a context-param element in the deployment descriptor:


Remember that the context-param elements appear before the filter, filter-mapping, listener, and servlet elements in the web.xml version for servlet API 2.3.

The code then gets the ServletOutputStream from the HttpServletResponse object. The binary data representing the PDF is written to this stream:

stream = response.getOutputStream( );

The servlet does not use a java.io.PrintWriter as in response.getWriter( ), because a PrintWriter is designed for returning character data, such as HTML, that the browser displays on the computer screen. Example 13-1 adds the response headers that help prevent the browsers from trying to display the bytes as content in the browser window:

        "Content-Disposition","attachment; filename="+fileName );
response.setContentLength( (int) pdf.length( ) );

The Content-Disposition header field signals the client to treat the received content as an attachment, not as characters to be displayed in the browser. This optional response header also provides a recommended filename, which the browser may include as the default filename in any "Save As" windows.

See RFC 2183 at ftp://ftp.rfc-editor.org/in-notes/rfc2183.txt for background information on the Content-Disposition header.

The client browser can use the Content-Length header value (provided with response.setContentLength( )) to indicate to the user the download progress with a widget that shows a horizontal bar steadily filling with color. The servlet also uses a java.io.BufferedInputStream to buffer the input from the file in a byte[] array, which speeds up the transfer of data from the server to the client.

See Recipe 13.5 for an example of using a java.net.URLConnection (as opposed to a FileInputStream) to get an input stream associated with a web resource. A URLConnection is useful when you want to obtain binary data from a PDF file that is available only as a web address beginning with "http://".

The code closes the ServletOutputStream and the BufferedInputStream in a finally block to release any system resources used by these objects. The code within the finally block executes regardless of whether the code throws an exception.

Internet Explorer 5.5 usually raises an exception that is displayed in the Tomcat log file when a request is made to this recipe's servlet. The logged exception does not disrupt the application, nor does it appear when the servlet is requested by Opera 5, or by Internet Explorer 5.2 and the Safari Macintosh browsers. The exception message includes the text "Connection reset by peer: socket write error." This message has raised speculation on various servlet-related mailing lists that the IE client browser on Windows has caused the exception by severing the connection with Tomcat after the data transfer. Nobody has yet devised a definitive solution to this apparently harmless exception, beyond suggesting that the servlet container's logging mechanism be configured to ignore exceptions of this type.

See Also

Recipe 13.2-Recipe 13.4 on sending Word, XML, and MP3 files as binary data; Recipe 13.5 on getting an input stream representing a web resource such as web.xml; RFC technical documents on MIME: ftp://ftp.rfc-editor.org/in-notes/rfc2045.txt and ftp://ftp.rfc-editor.org/in-notes/rfc2046.txt.; RFC 2183 at ftp://ftp.rfc-editor.org/in-notes/rfc2183.txt for background information on the Content-Disposition header; the Media Types section of the HTTP Pocket Reference by Clinton Long (O'Reilly).

    [ Team LiB ] Previous Section Next Section