前言
最近在弄文件上传、下载、在线预览时经常需要设置请求标头或者响应标头的Content-Type 属性。所以研究了一下spring支持哪些Content-Type,通过研究MediaTypeFactory.getMediaType的源码,可以得知spring是将支持的Content-Type 维护在/org/springframework/http/mime.types文件中。
private static MultiValueMap<String, MediaType> parseMimeTypes() {
InputStream is = MediaTypeFactory.class.getResourceAsStream("/org/springframework/http/mime.types");
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.US_ASCII));
Throwable var2 = null;
try {
MultiValueMap<String, MediaType> result = new LinkedMultiValueMap();
label111:
while(true) {
String line;
if ((line = reader.readLine()) != null) {
if (line.isEmpty() || line.charAt(0) == '#') {
continue;
}
String[] tokens = StringUtils.tokenizeToStringArray(line, " \t\n\r\f");
MediaType mediaType = MediaType.parseMediaType(tokens[0]);
int i = 1;
while(true) {
if (i >= tokens.length) {
continue label111;
}
String fileExtension = tokens[i].toLowerCase(Locale.ENGLISH);
result.add(fileExtension, mediaType);
++i;
}
}
LinkedMultiValueMap var5 = result;
return var5;
}
} catch (Throwable var17) {
var2 = var17;
throw var17;
} finally {
if (reader != null) {
if (var2 != null) {
try {
reader.close();
} catch (Throwable var16) {
var2.addSuppressed(var16);
}
} else {
reader.close();
}
}
}
} catch (IOException var19) {
throw new IllegalStateException("Could not load '/org/springframework/http/mime.types'", var19);
}
}
通过上面查找文件的路径找到了org.springframework:spring-web包下http目录中的mime.types文件
结果打开后居然发现 有多达1838个Content-Type,不过许多都是被注释掉的。过滤掉被注释的最终能被获取出来的一共是982个,本着好记性不如烂笔头的精神,我把这982个Content-Type已经它对应的文件后缀,整理了一下,形成了下面的表格,也方便以后查阅。
mime.types文件:
spring-web将mime.types文件加载到这个map里
spring-web里支持的文件以及对应的Content-Type
如何获取文件的Content-Type
1、使用spring-web包下MediaTypeFactory
MediaTypeFactory是spring-web包下的工具类,需要在pom引用spring-web包,本例使用的是:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version> 2.2.4.RELEASE</version>
</dependency>
从源码中可以看到MediaTypeFactory一共提供了三个对外方法,支持通过传入文件名或者Resource对象来获取Content-Type:
public static Optional<MediaType> getMediaType(@Nullable Resource resource) {
return Optional.ofNullable(resource).map(Resource::getFilename).flatMap(MediaTypeFactory::getMediaType);
}
public static Optional<MediaType> getMediaType(@Nullable String filename) {
return getMediaTypes(filename).stream().findFirst();
}
public static List<MediaType> getMediaTypes(@Nullable String filename) {
Optional var10000 = Optional.ofNullable(StringUtils.getFilenameExtension(filename)).map((s) -> {
return s.toLowerCase(Locale.ENGLISH);
});
MultiValueMap var10001 = fileExtensionToMediaTypes;
var10001.getClass();
return (List)var10000.map(var10001::get).orElse(Collections.emptyList());
}
我们用传入文件名的方式测试一下:
public static void main(String[] args) {
MediaType mediaType = MediaTypeFactory.getMediaType("测试.pdf").get();
String mediaTypeString = mediaType.toString();
System.out.println(mediaTypeString);
}
输出结果:
application/pdf
2、使用Apache Tika
Apache Tika 是一个内容分析工具包,可以检测上千种文件类型,并提取它们的元数据和文本。tika在设计上十分精巧,单一的接口使它易于使用,在搜索引擎索引,内容分析,翻译等诸多方面得到了广泛使用。
使用Apache Tika首先需要在pom引用:
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>1.28.4</version>
</dependency>
获取Content-Type 主要是用到其中detect的通过源码我们可以得知(源码太长了就不放了)detect支持通过传入String(文件名)、URL、File、Path、byte[]、InputStream等类型来解析以获取Content-Type
我们用传入byte[]的方式测试一下:
public static void main(String[] args) {
File file = new File("D:\\书籍\\电子书\\其它\\自然哲学的数学原理.pdf");
byte[] fileContent = new byte[(int) file.length()];
try (FileInputStream inputStream = new FileInputStream(file)) {
inputStream.read(fileContent);
} catch (IOException e) {
e.printStackTrace();
}
Tika tika = new Tika();
String mediaTypeString = tika.detect(fileContent);
System.out.println(mediaTypeString);
}
输出结果:
application/pdf
注意tika.detect返回的是String类型,如果想像第一种方式一样得到MediaType对象,还需要转换一下。
MediaType.parseMediaType(mediaTypeString);