FileContent.java

  1. /*
  2.  * FileContent
  3.  */
  4. package gov.usgs.earthquake.product;

  5. import gov.usgs.util.StreamUtils;

  6. import java.io.File;
  7. import java.io.InputStream;
  8. import java.io.IOException;
  9. import java.io.OutputStream;
  10. import java.net.URISyntaxException;

  11. import java.util.Date;
  12. import java.util.Map;
  13. import java.util.HashMap;
  14. import java.util.regex.Pattern;

  15. import javax.activation.MimetypesFileTypeMap;

  16. /**
  17.  * Content stored in a file.
  18.  */
  19. public class FileContent extends AbstractContent {

  20.     /** Used to look up file types. */
  21.     private static MimetypesFileTypeMap SYSTEM_MIME_TYPES = new MimetypesFileTypeMap();

  22.     /** Explicit list of file extensions with standard mime types. */
  23.     private static Map<String, String> MIME_TYPES = new HashMap<String, String>();
  24.     static {
  25.         MIME_TYPES.put("atom", "application/atom+xml");
  26.         MIME_TYPES.put("css", "text/css");
  27.         MIME_TYPES.put("gif", "image/gif");
  28.         MIME_TYPES.put("gz", "application/gzip");
  29.         MIME_TYPES.put("html", "text/html");
  30.         MIME_TYPES.put("jpg", "image/jpeg");
  31.         MIME_TYPES.put("js", "text/javascript");
  32.         MIME_TYPES.put("json", "application/json");
  33.         MIME_TYPES.put("kml", "application/vnd.google-earth.kml+xml");
  34.         MIME_TYPES.put("kmz", "application/vnd.google-earth.kmz");
  35.         MIME_TYPES.put("pdf", "application/pdf");
  36.         MIME_TYPES.put("png", "image/png");
  37.         MIME_TYPES.put("ps", "application/postscript");
  38.         MIME_TYPES.put("txt", "text/plain");
  39.         MIME_TYPES.put("xml", "application/xml");
  40.         MIME_TYPES.put("zip", "application/zip");
  41.     }

  42.     /** The actual content. */
  43.     private File content;

  44.     /**
  45.      * Construct a new FileContent that does not use a nested path. same as new
  46.      * FileContent(file, file.getParentFile());
  47.      *
  48.      * @param file
  49.      *            the source of content.
  50.      */
  51.     public FileContent(final File file) {
  52.         this.content = file;
  53.         this.setLastModified(new Date(file.lastModified()));
  54.         this.setLength(file.length());
  55.         this.setContentType(getMimeType(file));
  56.     }

  57.     /**
  58.      * Construct a new FileContent from a URLContent for legacy products
  59.      *
  60.      * @param urlc
  61.      *            the source of content.
  62.      * @throws URISyntaxException if error in URI
  63.      */
  64.     public FileContent(final URLContent urlc) throws URISyntaxException {
  65.         super(urlc);
  66.         this.content = new File(urlc.getURL().toURI());
  67.     }

  68.     /**
  69.      * Convert a Content to a file backed content.
  70.      *
  71.      * The file written is new File(baseDirectory, content.getPath()).
  72.      *
  73.      * @param content
  74.      *            the content that will be converted to a file.
  75.      * @param toWrite
  76.      *            the file where content is written.
  77.      * @throws IOException
  78.      *            if IO error occurs
  79.      */
  80.     public FileContent(final Content content, final File toWrite)
  81.             throws IOException {
  82.         super(content);

  83.         // this file content object will use the newly created file
  84.         this.content = toWrite;

  85.         // make sure the parent directory exists
  86.         File parent = toWrite.getCanonicalFile().getParentFile();
  87.         if (!parent.isDirectory()) {
  88.             parent.mkdirs();
  89.         }

  90.         // save handle to stream to force it closed
  91.         OutputStream out = null;
  92.         InputStream in = null;
  93.         try {
  94.             in = content.getInputStream();
  95.             out = StreamUtils.getOutputStream(toWrite);
  96.             // write the file
  97.             StreamUtils.transferStream(in, out);
  98.         } finally {
  99.             // force the stream closed
  100.             StreamUtils.closeStream(in);
  101.             StreamUtils.closeStream(out);
  102.         }

  103.         // update modification date in filesystem
  104.         toWrite.setLastModified(content.getLastModified().getTime());

  105.         // verify file length
  106.         Long length = getLength();
  107.         if (length > 0 && !length.equals(toWrite.length())) {
  108.             throw new IOException("Written file length ("
  109.                     + toWrite.length()
  110.                     + ") does not match non-zero content length (" + length
  111.                     + ")");
  112.         }

  113.         // length may still be <= 0 if content was input stream.
  114.         setLength(toWrite.length());
  115.     }

  116.     /**
  117.      * @return an InputStream for the wrapped content.
  118.      */
  119.     public InputStream getInputStream() throws IOException {
  120.         return StreamUtils.getInputStream(content);
  121.     }

  122.     /**
  123.      * @return the wrapped file.
  124.      */
  125.     public File getFile() {
  126.         return content;
  127.     }

  128.     /**
  129.      * Search a directory for files. This is equivalent to
  130.      * getDirectoryContents(directory, directory).
  131.      *
  132.      * @param directory
  133.      *            the directory to search.
  134.      * @return a map of relative paths to FileContent objects.
  135.      * @throws IOException if IO error occurs
  136.      */
  137.     public static Map<String, FileContent> getDirectoryContents(
  138.             final File directory) throws IOException {
  139.         File absDirectory = directory.getCanonicalFile();
  140.         return getDirectoryContents(absDirectory, absDirectory);
  141.     }

  142.     /**
  143.      * Search a directory for files. The path to files relative to baseDirectory
  144.      * is used as a key in the returned map.
  145.      *
  146.      * @param directory
  147.      *            the directory to search.
  148.      * @param baseDirectory
  149.      *            the directory used to compute relative paths.
  150.      * @return a map of relative paths to FileContent objects.
  151.      * @throws IOException if IO error occurs
  152.      */
  153.     public static Map<String, FileContent> getDirectoryContents(
  154.             final File directory, final File baseDirectory) throws IOException {
  155.         Map<String, FileContent> contents = new HashMap<String, FileContent>();

  156.         // compute the base path once, and escape the pattern being matched
  157.         String basePath = Pattern.quote(baseDirectory.getCanonicalPath()
  158.                 + File.separator);

  159.         File[] files = directory.listFiles();
  160.         for (File file : files) {
  161.             if (file.isDirectory()) {
  162.                 // recurse into sub directory
  163.                 contents.putAll(getDirectoryContents(file.getCanonicalFile(),
  164.                         baseDirectory.getCanonicalFile()));
  165.             } else {
  166.                 String path = file.getCanonicalPath().replaceAll(basePath, "");
  167.                 contents.put(path, new FileContent(file));
  168.             }
  169.         }

  170.         return contents;
  171.     }

  172.     /**
  173.      * This implementation calls defaultGetMimeType, and exists so subclasses
  174.      * can override.
  175.      *
  176.      * @param file
  177.      *            file to check.
  178.      * @return corresponding mime type.
  179.      */
  180.     public String getMimeType(final File file) {
  181.         return defaultGetMimeType(file);
  182.     }

  183.     /**
  184.      * Check a local list of mime types, and fall back to MimetypeFileTypesMap
  185.      * if not specified.
  186.      *
  187.      * @param file
  188.      *            file to check.
  189.      * @return corresponding mime type.
  190.      */
  191.     protected static String defaultGetMimeType(final File file) {
  192.         String name = file.getName();
  193.         int index = name.lastIndexOf('.');
  194.         if (index != -1) {
  195.             String extension = name.substring(index + 1);
  196.             if (MIME_TYPES.containsKey(extension)) {
  197.                 return MIME_TYPES.get(extension);
  198.             }
  199.         }
  200.         return SYSTEM_MIME_TYPES.getContentType(file);
  201.     }

  202.     /**
  203.      * Free any resources associated with this content.
  204.      */
  205.     public void close() {
  206.         // nothing to free
  207.     }

  208. }