eu.medsea.mimeutil.detector.ExtensionMimeDetector Maven / Gradle / Ivy
/*
* Copyright 2007-2009 Medsea Business Solutions S.L.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.medsea.mimeutil.detector;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import eu.medsea.mimeutil.MimeException;
import eu.medsea.mimeutil.MimeType;
import eu.medsea.mimeutil.MimeUtil;
/**
*
* The extension mime mappings are loaded in the following way.
*
* - Load the properties file from the mime utility jar named
*
eu.medsea.mimeutil.mime-types.properties
.
* - Locate and load a file named
.mime-types.properties
from the
* users home directory if one exists.
* - Locate and load a file named
mime-types.properties
from the
* classpath if one exists
* - locate and load a file named by the JVM property
*
mime-mappings
i.e.
* -Dmime-mappings=../my-mime-types.properties
*
* Each property file loaded will add to the list of extensions understood by MimeUtil.
* If there is a clash of extension names then the last one loaded wins, i.e they are not adative, this makes it
* possible to completely change the mime types associated to a file extension declared in previously loaded property files.
* The extensions are also case sensitive meaning that bat, bAt, BAT and Bat can all be recognised individually. If however,
* no match is found using case sensitive matching then it will perform an insensitive match by lower casing the extension
* of the file to be matched first.
*
*
* Fortunately, we have compiled a relatively large list of mappings into a java properties file from information gleaned from many sites on the Internet.
* This file resides in the eu.medsea.util.mime-types.properties file and is not guaranteed to be correct or contain all the known mappings for a file
* extension type. This is not a complete or exhaustive list as that would have proven too difficult to compile for this project.
* So instead we give you the opportunity to extend and override these mappings for yourself as defined above.
* Obviously, to use this method you don't actually need a file object, you just need a file name with an extension. Also, if you have given or renamed a
* file using a different extension than the one that it would normally be associated with then this mapping will return the wrong mime-type and
* if the file has no extension at all, such as Make, then it's not going to be possible to determine a mime type using this technique
*
*
* We acquired many mappings from many different sources on the net for the
* extension mappings. The internal list is quite large and there can be many
* associated mime types. These may not match what you are expecting so you can
* add the mapping you want to change to your own property file following the
* rules above. If you provide a mapping for an extension then any previously
* loaded mappings will be removed and only the mappings you define will be
* returned. This can be used to map certain extensions that are incorrectly
* returned for our environment defined in the internal property file.
*
*
* If we have not provided a mapping for a file extension that you know the mime
* type for you can add this to your custom property files so that a correct mime
* type is returned for you.
*
*
* We use the application/directory
mime type to identify
* directories. Even though this is not an official mime type it seems to be
* well accepted on the net as an unofficial mime type so we thought it was OK
* for us to use as well.
*
*
* This class is auto loaded by MimeUtil as it has an entry in the file called MimeDetectors.
* MimeUtil reads this file at startup and calls Class.forName() on each entry found. This mean
* the MimeDetector must have a no arg constructor.
*
*
* @author Steven McArdle.
*
*/
public class ExtensionMimeDetector extends MimeDetector {
private static Log log = LogFactory.getLog(ExtensionMimeDetector.class);
// Initialise this MimeDetector and automatically register it with MimeUtil
static {
initMimeTypes();
MimeUtil.addMimeDetector(new ExtensionMimeDetector());
}
// Extension MimeTypes
private static Map extMimeTypes;
public ExtensionMimeDetector() {}
public String getDescription() {
return "Get the mime types of file extensions";
}
/**
* Get the mime type of a file using file extension mappings. The file path
* can be a relative or absolute path or can be a completely non-existent file as
* only the extension is important here.
*
* @param file
* is a File
object that points to a file or
* directory.
* @return collection of the matched mime types.
* @throws MimeException
* if the file cannot be parsed.
*/
public Collection getMimeTypesFile(final File file) throws MimeException {
Collection mimeTypes = new HashSet();
String fileExtension = MimeUtil.getExtension(file.getName());
// First try case insensitive match
String types = (String) extMimeTypes.get(fileExtension);
if (types != null) {
String [] mimeTypeArray = types.split(",");
for(int i = 0; i < mimeTypeArray.length; i++) {
mimeTypes.add(new MimeType(mimeTypeArray[i]));
}
}
if(mimeTypes.isEmpty()) {
// Failed to find case insensitive extension so lets try again with
// lowercase
types = (String) extMimeTypes.get(fileExtension.toLowerCase());
if (types != null) {
String [] mimeTypeArray = types.split(",");
for(int i = 0; i < mimeTypeArray.length; i++) {
mimeTypes.add(new MimeType(mimeTypeArray[i]));
}
}
}
return mimeTypes;
}
/*
* This loads the mime-types.properties files that define mime types based
* on file extensions using the following load sequence 1. Loads the
* property file from the mime utility jar named
* eu.medsea.mime.mime-types.properties. 2. Locates and loads a file named
* .mime-types.properties from the users home directory if one exists. 3.
* Locates and loads a file named mime-types.properties from the classpath
* if one exists 4. locates and loads a file named by the JVM property
* mime-mappings i.e. -Dmime-mappings=../my-mime-types.properties
*/
private static void initMimeTypes() {
extMimeTypes = new Properties();
// Load the file extension mappings from the internal property file and
// then
// from the custom property files if they can be found
try {
// Load the default supplied mime types
((Properties) extMimeTypes)
.load(MimeUtil.class.getClassLoader().getResourceAsStream(
"eu/medsea/mimeutil/mime-types.properties"));
// Load any .mime-types.properties from the users home directory
try {
File f = new File(System.getProperty("user.home")
+ File.separator + ".mime-types.properties");
if (f.exists()) {
InputStream is = new FileInputStream(f);
if (is != null) {
log
.debug("Found a custom .mime-types.properties file in the users home directory.");
Properties props = new Properties();
props.load(is);
if (props.size() > 0) {
extMimeTypes.putAll(props);
}
log
.debug("Successfully parsed .mime-types.properties from users home directory.");
}
}
} catch (Exception e) {
log
.error(
"Failed to parse .magic.mime file from users home directory. File will be ignored.",
e);
}
try {
// Load any classpath provided mime types that either extend or
// override the default mime type entries
InputStream is = MimeUtil.class.getClassLoader()
.getResourceAsStream("mime-types.properties");
if (is != null) {
log
.debug("Found a custom mime-types.properties file on the classpath.");
Properties props = new Properties();
props.load(is);
if (props.size() > 0) {
extMimeTypes.putAll(props);
}
log
.debug("Successfully loaded custome mime-type.properties file from classpath.");
}
} catch (Exception e) {
log
.error("Failed to load the mime-types.properties file located on the classpath. File will be ignored.");
}
try {
// Load any mime extension mappings file defined with the JVM
// property -Dmime-mappings=../my/custom/mappings.properties
String fname = System.getProperty("mime-mappings");
if (fname != null && fname.length() != 0) {
InputStream is = new FileInputStream(fname);
if (is != null) {
if (log.isDebugEnabled()) {
log
.debug("Found a custom mime-mappings property defined by the property -Dmime-mappings ["
+ System
.getProperty("mime-mappings")
+ "].");
}
Properties props = new Properties();
props.load(is);
if (props.size() > 0) {
extMimeTypes.putAll(props);
}
log
.debug("Successfully loaded the mime mappings file from property -Dmime-mappings ["
+ System.getProperty("mime-mappings")
+ "].");
}
}
} catch (Exception e) {
log
.error("Failed to load the mime-mappings file defined by the property -Dmime-mappings ["
+ System.getProperty("mime-mappings") + "].");
}
} catch (Exception e) {
// log the error but don't throw the exception up the stack
log.error("Error loading internal mime-types.properties", e);
} finally {
// Load the mime types into the known mime types map of MimeUtil
Iterator it = extMimeTypes.values().iterator();
while (it.hasNext()) {
String[] types = ((String) it.next()).split(",");
for (int i = 0; i < types.length; i++) {
MimeUtil.addKnownMimeType(types[i]);
}
}
}
}
/**
* This method is required by the abstract MimeDetector class. As we do not support extension mapping of streams
* we just throw an {@link UnsupportedOperationException}. This ensures that the getMimeTypes(...) methods ignore this
* method. We could also have just returned an empty collection.
*/
public Collection getMimeTypesInputStream(InputStream in)
throws UnsupportedOperationException {
throw new UnsupportedOperationException("This MimeDetector does not support detection from streams.");
}
/**
* This method is required by the abstract MimeDetector class. As we do not support extension mapping of byte arrays
* we just throw an {@link UnsupportedOperationException}. This ensures that the getMimeTypes(...) methods ignore this
* method. We could also have just returned an empty collection.
*/
public Collection getMimeTypesByteArray(byte[] data)
throws UnsupportedOperationException {
throw new UnsupportedOperationException("This MimeDetector does not support detection from byte arrays.");
}
}