001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.mail.javamail; 018 019import java.io.File; 020import java.io.IOException; 021import java.io.InputStream; 022 023import javax.activation.FileTypeMap; 024import javax.activation.MimetypesFileTypeMap; 025 026import org.springframework.beans.factory.InitializingBean; 027import org.springframework.core.io.ClassPathResource; 028import org.springframework.core.io.Resource; 029import org.springframework.lang.Nullable; 030 031/** 032 * Spring-configurable {@code FileTypeMap} implementation that will read 033 * MIME type to file extension mappings from a standard JavaMail MIME type 034 * mapping file, using a standard {@code MimetypesFileTypeMap} underneath. 035 * 036 * <p>The mapping file should be in the following format, as specified by the 037 * Java Activation Framework: 038 * 039 * <pre class="code"> 040 * # map text/html to .htm and .html files 041 * text/html html htm HTML HTM</pre> 042 * 043 * Lines starting with {@code #} are treated as comments and are ignored. All 044 * other lines are treated as mappings. Each mapping line should contain the MIME 045 * type as the first entry and then each file extension to map to that MIME type 046 * as subsequent entries. Each entry is separated by spaces or tabs. 047 * 048 * <p>By default, the mappings in the {@code mime.types} file located in the 049 * same package as this class are used, which cover many common file extensions 050 * (in contrast to the out-of-the-box mappings in {@code activation.jar}). 051 * This can be overridden using the {@code mappingLocation} property. 052 * 053 * <p>Additional mappings can be added via the {@code mappings} bean property, 054 * as lines that follow the {@code mime.types} file format. 055 * 056 * @author Rob Harrop 057 * @author Juergen Hoeller 058 * @since 1.2 059 * @see #setMappingLocation 060 * @see #setMappings 061 * @see javax.activation.MimetypesFileTypeMap 062 */ 063public class ConfigurableMimeFileTypeMap extends FileTypeMap implements InitializingBean { 064 065 /** 066 * The {@code Resource} to load the mapping file from. 067 */ 068 private Resource mappingLocation = new ClassPathResource("mime.types", getClass()); 069 070 /** 071 * Used to configure additional mappings. 072 */ 073 @Nullable 074 private String[] mappings; 075 076 /** 077 * The delegate FileTypeMap, compiled from the mappings in the mapping file 078 * and the entries in the {@code mappings} property. 079 */ 080 @Nullable 081 private FileTypeMap fileTypeMap; 082 083 084 /** 085 * Specify the {@code Resource} from which mappings are loaded. 086 * <p>Needs to follow the {@code mime.types} file format, as specified 087 * by the Java Activation Framework, containing lines such as:<br> 088 * {@code text/html html htm HTML HTM} 089 */ 090 public void setMappingLocation(Resource mappingLocation) { 091 this.mappingLocation = mappingLocation; 092 } 093 094 /** 095 * Specify additional MIME type mappings as lines that follow the 096 * {@code mime.types} file format, as specified by the 097 * Java Activation Framework. For example:<br> 098 * {@code text/html html htm HTML HTM} 099 */ 100 public void setMappings(String... mappings) { 101 this.mappings = mappings; 102 } 103 104 105 /** 106 * Creates the final merged mapping set. 107 */ 108 @Override 109 public void afterPropertiesSet() { 110 getFileTypeMap(); 111 } 112 113 /** 114 * Return the delegate FileTypeMap, compiled from the mappings in the mapping file 115 * and the entries in the {@code mappings} property. 116 * @see #setMappingLocation 117 * @see #setMappings 118 * @see #createFileTypeMap 119 */ 120 protected final FileTypeMap getFileTypeMap() { 121 if (this.fileTypeMap == null) { 122 try { 123 this.fileTypeMap = createFileTypeMap(this.mappingLocation, this.mappings); 124 } 125 catch (IOException ex) { 126 throw new IllegalStateException( 127 "Could not load specified MIME type mapping file: " + this.mappingLocation, ex); 128 } 129 } 130 return this.fileTypeMap; 131 } 132 133 /** 134 * Compile a {@link FileTypeMap} from the mappings in the given mapping file 135 * and the given mapping entries. 136 * <p>The default implementation creates an Activation Framework {@link MimetypesFileTypeMap}, 137 * passing in an InputStream from the mapping resource (if any) and registering 138 * the mapping lines programmatically. 139 * @param mappingLocation a {@code mime.types} mapping resource (can be {@code null}) 140 * @param mappings an array of MIME type mapping lines (can be {@code null}) 141 * @return the compiled FileTypeMap 142 * @throws IOException if resource access failed 143 * @see javax.activation.MimetypesFileTypeMap#MimetypesFileTypeMap(java.io.InputStream) 144 * @see javax.activation.MimetypesFileTypeMap#addMimeTypes(String) 145 */ 146 protected FileTypeMap createFileTypeMap(@Nullable Resource mappingLocation, @Nullable String[] mappings) throws IOException { 147 MimetypesFileTypeMap fileTypeMap = null; 148 if (mappingLocation != null) { 149 InputStream is = mappingLocation.getInputStream(); 150 try { 151 fileTypeMap = new MimetypesFileTypeMap(is); 152 } 153 finally { 154 is.close(); 155 } 156 } 157 else { 158 fileTypeMap = new MimetypesFileTypeMap(); 159 } 160 if (mappings != null) { 161 for (String mapping : mappings) { 162 fileTypeMap.addMimeTypes(mapping); 163 } 164 } 165 return fileTypeMap; 166 } 167 168 169 /** 170 * Delegates to the underlying FileTypeMap. 171 * @see #getFileTypeMap() 172 */ 173 @Override 174 public String getContentType(File file) { 175 return getFileTypeMap().getContentType(file); 176 } 177 178 /** 179 * Delegates to the underlying FileTypeMap. 180 * @see #getFileTypeMap() 181 */ 182 @Override 183 public String getContentType(String fileName) { 184 return getFileTypeMap().getContentType(fileName); 185 } 186 187}