Class MimeTypeDetector

java.lang.Object
org.overviewproject.mime_types.MimeTypeDetector

public class MimeTypeDetector extends Object
Determines the MIME type of files.

The Opendesktop shared mime database contains glob rules and magic number lookup information to enable applications to detect the mime types of files.

For a complete description of the information contained in this file please see shared-mime-info-spec

  • Field Details

    • MIME_CACHE

      private static final String MIME_CACHE
      See Also:
    • content

      private final ByteBuffer content
      MimeCache file content, as a ByteBuffer.
    • contentBytes

      private final byte[] contentBytes
      MimeCache file content, as an array of bytes.
  • Constructor Details

    • MimeTypeDetector

      public MimeTypeDetector()
      Creates a new MimeTypeDetector.

      This class is thread-safe. You should create it once and reuse it, as it allocates a bit of memory (hundreds of kilobytes) on load.

  • Method Details

    • inputStreamToByteArray

      private byte[] inputStreamToByteArray(InputStream is) throws IOException
      Throws:
      IOException
    • unwrapFutureString

      private static String unwrapFutureString(CompletionStage<String> future) throws GetBytesException
      Waits for a CompletionStage, for non-async methods.
      Throws:
      GetBytesException
    • detectMimeTypeAsync

      public CompletionStage<String> detectMimeTypeAsync(String filename, Supplier<CompletionStage<byte[]>> getBytesAsync)
      Detects a MIME type from a filename and bytes.

      This method follows the Shared Mime Info database's Recommended Checking Order. The only difference: it tests for text/plain thoroughly, both by scanning more of the file and by supporting many character sets.

      getBytes() is a Callable because it may or may not be needed. A file named README will always be detected as text/plain, for instance; a file named foo.doc will need a magic-number check because it may be plain text or it may be a Word document.

      If you are creating your own getBytesAsync method, ensure its return value is unpadded. (Use Arrays.copyOf(byte[],int) to truncate it.) It needn't be any longer than getMaxGetBytesLength() bytes.

      The CompletionStage may return a ExecutionException which is caused by a GetBytesException. (That, in turn, will wrap a IOException or other exception that prevented getBytesAsync() from working.

      Parameters:
      filename - Filename. To skip filename globbing, pass ""
      getBytesAsync - Supplier that eventually returns a byte[]
      Returns:
      Eventual MIME type String, falling back to "application/octet-stream"
    • detectMimeType

      public String detectMimeType(String filename, Callable<byte[]> getBytes) throws GetBytesException
      Synchronously detects a MIME type from a filename and bytes.

      This method follows the Shared Mime Info database's Recommended Checking Order. The only difference: it tests for text/plain thoroughly, both by scanning more of the file and by supporting many character sets.

      getBytes() is a Callable because it may or may not be needed. A file named README will always be detected as text/plain, for instance; a file named foo.doc will need a magic-number check because it may be plain text or it may be a Word document.

      If you are creating your own getBytes method, ensure its return value is unpadded. (Use Arrays.copyOf(byte[],int) to truncate it.) It needn't be any longer than getMaxGetBytesLength() bytes.

      Parameters:
      filename - Filename. To skip filename globbing, pass ""
      getBytes - Callable that returns a byte[]
      Returns:
      a MIME type such as "text/plain"
      Throws:
      GetBytesException - if getBytes.call() throws an Exception
    • detectMimeType

      public String detectMimeType(String filename, InputStream is) throws GetBytesException
      Determines the MIME type of file with a given input stream.

      The InputStream must exist. It must point to the beginning of the file contents. And InputStream.markSupported() must return true. When in doubt, pass a BufferedInputStream.

      Parameters:
      filename - Name of file. To skip filename globbing, pass ""
      is - InputStream that supports mark and reset.
      Returns:
      a MIME type such as "text/plain"
      Throws:
      GetBytesException - if marking, reading or resetting the InputStream fails.
      See Also:
    • detectMimeType

      public String detectMimeType(File file) throws GetBytesException
      Determines the MIME type of file.

      The file must exist and be readable.

      Parameters:
      file - A file that exists and is readable
      Returns:
      a MIME type such as "text/plain"
      Throws:
      GetBytesException - if reading the file fails.
      See Also:
    • detectMimeType

      public String detectMimeType(Path path) throws GetBytesException
      Determines the MIME type of file.

      The file must exist and be readable.

      Parameters:
      path - A file that exists and is readable
      Returns:
      a MIME type such as "text/plain"
      Throws:
      GetBytesException - if reading the file fails.
      See Also:
    • detectMimeTypeAsync

      public CompletionStage<String> detectMimeTypeAsync(Path path)
      Determines the MIME type of file.

      The file must exist and be readable.

      The CompletionStage may return a ExecutionException which is caused by a GetBytesException. (That, in turn, will wrap a IOException or other exception that prevented getBytesAsync() from working.

      Parameters:
      path - A file that exists and is readable
      Returns:
      a MIME type such as "text/plain"
      See Also:
    • isText

      private boolean isText(byte[] bytes)
    • isEncodedText

      private boolean isEncodedText(byte[] bytes)
    • inputStreamToFirstBytes

      private byte[] inputStreamToFirstBytes(InputStream is) throws IOException
      Throws:
      IOException
    • bytesToMimeTypes

      private Iterable<String> bytesToMimeTypes(byte[] data)
    • compareToMagicData

      private String compareToMagicData(int offset, byte[] data)
    • matchletMagicCompareOr

      private boolean matchletMagicCompareOr(int nMatchlets, int firstMatchletOffset, byte[] data)
      Returns whether one of the specified matchlets matches the data.
    • subArraysEqual

      private boolean subArraysEqual(byte[] a, int aStart, byte[] b, int bStart, int len)
      Returns true if subarrays are equal.
    • subArraysEqualWithMask

      private boolean subArraysEqualWithMask(byte[] a, int aStart, byte[] b, int bStart, byte[] mask, int maskStart, int len)
      Returns true if subarrays are equal, with the given mask.

      The mask must have length len.

    • oneMatchletMagicEquals

      private boolean oneMatchletMagicEquals(int offset, byte[] data)
      Tests whether one matchlet (but not its children) matches data.
    • matchletMagicCompare

      private boolean matchletMagicCompare(int offset, byte[] data)
      Returns whether data satisfies the matchlet and its children.
    • filenameToWmtOrNullByLiteral

      private MimeTypeDetector.WeightedMimeType filenameToWmtOrNullByLiteral(String filename)
    • filenameToWmtsByGlob

      private Set<MimeTypeDetector.WeightedMimeType> filenameToWmtsByGlob(String filename)
    • findBestMimeTypes

      private Set<String> findBestMimeTypes(Collection<MimeTypeDetector.WeightedMimeType> weightedMimeTypes)
    • filenameToWmts

      private Set<MimeTypeDetector.WeightedMimeType> filenameToWmts(String filename)
    • filenameToWmtsOrNullBySuffixAndIgnoreCase

      private Set<MimeTypeDetector.WeightedMimeType> filenameToWmtsOrNullBySuffixAndIgnoreCase(String filename, boolean ignoreCase)
    • lookupGlobNodeSuffix

      private void lookupGlobNodeSuffix(String fileName, int numEntries, int offset, boolean ignoreCase, int len, Set<MimeTypeDetector.WeightedMimeType> mimeTypes, StringBuilder pattern)
    • getMaxExtents

      private int getMaxExtents()
    • getMaxGetBytesLength

      public int getMaxGetBytesLength()
      Returns the number of bytes the magic number sniffers may read.

      If you are crafting your own getBytes() method, you may use this as a hint. getBytes() may return an Array with any number of bytes you like, but MimeTypeDetector will not read more than this many.

      Returns:
      The most bytes getBytes() could ever possibly need to return.
    • aliasLookup

      private String aliasLookup(String alias)
    • unaliasMimeType

      private String unaliasMimeType(String mimeType)
    • isMimeTypeEqualOrSubclass

      private boolean isMimeTypeEqualOrSubclass(String child, String parent)
    • getMagicListOffset

      private int getMagicListOffset()
    • getGlobListOffset

      private int getGlobListOffset()
    • getReverseSuffixTreeOffset

      private int getReverseSuffixTreeOffset()
    • getLiteralListOffset

      private int getLiteralListOffset()
    • getParentListOffset

      private int getParentListOffset()
    • getAliasListOffset

      private int getAliasListOffset()
    • getMimeType

      private String getMimeType(int offset)
    • getString

      private String getString(int offset)
    • getRegex

      private String getRegex(int offset)
    • getStringOrRegex

      private String getStringOrRegex(int offset, boolean regularExpression)