Class AbstractFileServlet

java.lang.Object
javax.servlet.GenericServlet
javax.servlet.http.HttpServlet
org.openremote.container.web.file.AbstractFileServlet
All Implemented Interfaces:
Serializable, javax.servlet.Servlet, javax.servlet.ServletConfig
Direct Known Subclasses:
FileServlet

public abstract class AbstractFileServlet extends javax.servlet.http.HttpServlet

The well known "BalusC FileServlet", as an abstract template, slightly refactored, rewritten and modernized with a.o. fast NIO stuff instead of legacy RandomAccessFile. GZIP support is stripped off as that can be done application wide via GzipResponseFilter.

This servlet properly deals with ETag, If-None-Match and If-Modified-Since caching requests, hereby improving browser caching. This servlet also properly deals with Range and If-Range ranging requests (RFC7233), which is required by most media players for proper audio/video streaming, and by webbrowsers and for a proper resume of an paused download, and by download accelerators to be able to request smaller parts simultaneously. This servlet is ideal when you have large files like media files placed outside the web application and you can't use the default servlet.

Usage

Just extend this class and override the getFile(HttpServletRequest) method to return the desired file. If you want to trigger a HTTP 400 "Bad Request" error, simply throw IllegalArgumentException. If you want to trigger a HTTP 404 "Not Found" error, simply return null, or a non-existent file.

Here's a concrete example which serves it via an URL like /media/foo.ext:

 @WebServlet("/media/*")
 public class MediaFileServlet extends FileServlet {

     private File folder;

     @Override
     public void init() throws ServletException {
         folder = new File("/var/webapp/media");
     }

     @Override
     protected File getFile(HttpServletRequest request) {
         String pathInfo = request.getPathInfo();

         if (pathInfo == null || pathInfo.isEmpty() || "/".equals(pathInfo)) {
             throw new IllegalArgumentException();
         }

         return new File(folder, pathInfo);
     }

 }
 

You can embed it in e.g. HTML5 video tag as below:

 <video src="#{request.contextPath}/media/video.mp4" controls="controls" />
 

Customizing FileServlet

If more fine grained control is desired for handling "file not found" error, determining the cache expire time, the content type, whether the file should be supplied as an attachment and the attachment's file name, then the developer can opt to override one or more of the following protected methods:

See also:

Since:
2.2
See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static class 
    Convenience class for a byte range.
    protected class 
     
    static class 
    Convenience class for a file resource.
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    static <T> T
    coalesce(T... objects)
    Returns the first non-null object of the argument list, or null if there is no such element.
    protected void
    doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
     
    protected void
    doHead(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
     
    static String
    encodeURI(String string)
    URI-encode the given string using UTF-8.
    static String
    encodeURL(String string)
    URL-encode the given string using UTF-8.
    protected String
    getContentType(javax.servlet.http.HttpServletRequest request, File file)
    Returns the content type associated with the given HTTP servlet request and file.
    protected long
    getExpireTime(javax.servlet.http.HttpServletRequest request, File file)
    Returns how long the resource may be cached by the client before it expires, in seconds.
    protected abstract File
    getFile(javax.servlet.http.HttpServletRequest request)
    Returns the file associated with the given HTTP servlet request.
    protected void
    handleFileNotFound(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
    Handles the case when the file is not found.
    static void
    setCacheHeaders(javax.servlet.http.HttpServletResponse response, long expires)
    Set the cache headers.
    protected String
    setContentHeaders(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, AbstractFileServlet.Resource resource, List<AbstractFileServlet.Range> ranges)
    Set content headers.
    static void
    setNoCacheHeaders(javax.servlet.http.HttpServletResponse response)
    Set the no-cache headers.
    static boolean
    startsWithOneOf(String string, String... prefixes)
    Returns true if the given string starts with one of the given prefixes.
    static long
    stream(File file, OutputStream output, long start, long length)
    Stream a specified range of the given file to the given output via NIO Channels and a directly allocated NIO ByteBuffer.
    static long
    Stream the given input to the given output via NIO Channels and a directly allocated NIO ByteBuffer.

    Methods inherited from class javax.servlet.http.HttpServlet

    doDelete, doOptions, doPost, doPut, doTrace, getLastModified, service, service

    Methods inherited from class javax.servlet.GenericServlet

    destroy, getInitParameter, getInitParameterNames, getServletConfig, getServletContext, getServletInfo, getServletName, init, init, log, log

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • AbstractFileServlet

      public AbstractFileServlet()
  • Method Details

    • doHead

      protected void doHead(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException
      Overrides:
      doHead in class javax.servlet.http.HttpServlet
      Throws:
      javax.servlet.ServletException
      IOException
    • doGet

      protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException
      Overrides:
      doGet in class javax.servlet.http.HttpServlet
      Throws:
      javax.servlet.ServletException
      IOException
    • getFile

      protected abstract File getFile(javax.servlet.http.HttpServletRequest request) throws IllegalArgumentException, AbstractFileServlet.RedirectException
      Returns the file associated with the given HTTP servlet request. If this method throws IllegalArgumentException, then the servlet will return a HTTP 400 error. If this method returns null, or if File.isFile() returns false, then the servlet will invoke handleFileNotFound(HttpServletRequest, HttpServletResponse).
      Parameters:
      request - The involved HTTP servlet request.
      Returns:
      The file associated with the given HTTP servlet request.
      Throws:
      IllegalArgumentException - When the request is mangled in such way that it's not recognizable as a valid file request. The servlet will then return a HTTP 400 error.
      AbstractFileServlet.RedirectException
    • handleFileNotFound

      protected void handleFileNotFound(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws IOException
      Handles the case when the file is not found.

      The default implementation sends a HTTP 404 error.

      Parameters:
      request - The involved HTTP servlet request.
      response - The involved HTTP servlet response.
      Throws:
      IOException - When something fails at I/O level.
      Since:
      2.3
    • getExpireTime

      protected long getExpireTime(javax.servlet.http.HttpServletRequest request, File file)
      Returns how long the resource may be cached by the client before it expires, in seconds.

      The default implementation returns 30 days in seconds.

      Parameters:
      request - The involved HTTP servlet request.
      file - The involved file.
      Returns:
      The client cache expire time in seconds (not milliseconds!).
    • getContentType

      protected String getContentType(javax.servlet.http.HttpServletRequest request, File file)
      Returns the content type associated with the given HTTP servlet request and file.

      The default implementation delegates File.getName() to ServletContext.getMimeType(String) with a fallback default value of application/octet-stream.

      Parameters:
      request - The involved HTTP servlet request.
      file - The involved file.
      Returns:
      The content type associated with the given HTTP servlet request and file.
    • setContentHeaders

      protected String setContentHeaders(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, AbstractFileServlet.Resource resource, List<AbstractFileServlet.Range> ranges)
      Set content headers.
    • encodeURI

      public static String encodeURI(String string)
      URI-encode the given string using UTF-8. URIs (paths and filenames) have different encoding rules as compared to URL query string parameters. URLEncoder is actually only for www (HTML) form based query string parameter values (as used when a webbrowser submits a HTML form). URI encoding has a lot in common with URL encoding, but the space has to be %20 and some chars doesn't necessarily need to be encoded.
      Parameters:
      string - The string to be URI-encoded using UTF-8.
      Returns:
      The given string, URI-encoded using UTF-8, or null if null was given.
      Throws:
      UnsupportedOperationException - When this platform does not support UTF-8.
      Since:
      2.4
    • encodeURL

      public static String encodeURL(String string)
      URL-encode the given string using UTF-8.
      Parameters:
      string - The string to be URL-encoded using UTF-8.
      Returns:
      The given string, URL-encoded using UTF-8, or null if null was given.
      Throws:
      UnsupportedOperationException - When this platform does not support UTF-8.
      Since:
      1.4
    • coalesce

      @SafeVarargs public static <T> T coalesce(T... objects)
      Returns the first non-null object of the argument list, or null if there is no such element.
      Type Parameters:
      T - The generic object type.
      Parameters:
      objects - The argument list of objects to be tested for non-null.
      Returns:
      The first non-null object of the argument list, or null if there is no such element.
    • startsWithOneOf

      public static boolean startsWithOneOf(String string, String... prefixes)
      Returns true if the given string starts with one of the given prefixes.
      Parameters:
      string - The object to be checked if it starts with one of the given prefixes.
      prefixes - The argument list of prefixes to be checked
      Returns:
      true if the given string starts with one of the given prefixes.
      Since:
      1.4
    • stream

      public static long stream(InputStream input, OutputStream output) throws IOException
      Stream the given input to the given output via NIO Channels and a directly allocated NIO ByteBuffer. Both the input and output streams will implicitly be closed after streaming, regardless of whether an exception is been thrown or not.
      Parameters:
      input - The input stream.
      output - The output stream.
      Returns:
      The length of the written bytes.
      Throws:
      IOException - When an I/O error occurs.
    • stream

      public static long stream(File file, OutputStream output, long start, long length) throws IOException
      Stream a specified range of the given file to the given output via NIO Channels and a directly allocated NIO ByteBuffer. The output stream will only implicitly be closed after streaming when the specified range represents the whole file, regardless of whether an exception is been thrown or not.
      Parameters:
      file - The file.
      output - The output stream.
      start - The start position (offset).
      length - The (intented) length of written bytes.
      Returns:
      The (actual) length of the written bytes. This may be smaller when the given length is too large.
      Throws:
      IOException - When an I/O error occurs.
      Since:
      2.2
    • setCacheHeaders

      public static void setCacheHeaders(javax.servlet.http.HttpServletResponse response, long expires)

      Set the cache headers. If the expires argument is larger than 0 seconds, then the following headers will be set:

      • Cache-Control: public,max-age=[expiration time in seconds],must-revalidate
      • Expires: [expiration date of now plus expiration time in seconds]

      Else the method will delegate to setNoCacheHeaders(HttpServletResponse).

      Parameters:
      response - The HTTP servlet response to set the headers on.
      expires - The expire time in seconds (not milliseconds!).
      Since:
      2.2
    • setNoCacheHeaders

      public static void setNoCacheHeaders(javax.servlet.http.HttpServletResponse response)

      Set the no-cache headers. The following headers will be set:

      • Cache-Control: no-cache,no-store,must-revalidate
      • Expires: [expiration date of 0]
      • Pragma: no-cache
      Set the no-cache headers.
      Parameters:
      response - The HTTP servlet response to set the headers on.
      Since:
      2.2