判断任意文件的真实文件类型相关-以Apache的tika依赖为例(附工具类代码)
程序员文章站
2024-02-19 12:18:34
...
闲来无事,老夫便查阅了功夫秘籍(曾经做过的项目)用过的关于解析真实文件类型的各种判断。方式虽各不相同,但却殊途同归,基本都会经过的几个步骤如下:
- 创建一个任意的Map集合,其中键为文件解析时的十六进制数前几位数值,值为该文件的真实文件类型字符串。
- 通过流或其他的形式(基本都是用流)读出文件携带的类似于文件摘要的字节数组,拼接成对应的十六进制字符串,即上述Map中的键数据。
- 将得到待判断文件的十六进制字符串结果同Map进行比对,如果存在,返回Map的值;不存在,返回null。
ps:个人认为每位同仁在自己写工具类时可能会存在以下缺点:1.在选择十六进制数值作为键时可能都不相同,会导致某些相同文件类型判断出的结果会不一致;2.根据业务需求不同,导致判断的文件类型相对局限,在别人使用这个不完全的工具类时并不能达到他的业务需求(亲身经历:选择csv直接给我返回null,不留任何余地);3.可能存在并发性风险,这是由于某些判断文件类型时的工具类中使用了MessageDigest类,并将它赋值提升为了全局变量,这是多线程情况下是不允许的!顺便多说一点,如果各位在多线程环境下,还是尽量使用局部的MessageDigest类吧,也就是说只要调用方法就用MessageDigest.getInstance(“xxx”)实例化该对象出来用即可。
针对以上我们自己书写但是却可能存在的“BUG”,Apache基金会为我们带来了Tika这个依赖,较为完美的解决了以上所有的问题,不过令人遗憾的是他也带来了一些新的问题,具体问题我会在本文末尾提出。
1.使用Tika也特别简单,首先我们需要在POM.XML中引入以下依赖,我这里用的是1.22版本,如果各位使用时能够联网的话,可以直接把exclusions标签中的内容都去掉,我这里加上的原因是项目环境原因。
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers</artifactId>
<version>1.22</version>
<exclusions>
<exclusion>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-xjc</artifactId>
</exclusion>
<exclusion>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</exclusion>
<exclusion>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
</exclusion>
<exclusion>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
</exclusion>
</exclusions>
</dependency>
2.将以下工具类引入至你的项目中
package com.tienon.boot.util;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.tika.detect.Detector;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.AutoDetectParser;
/**
* 检查文件的真实类型的工具类,但是无法识别csv格式的文件
*
* @author dfgz1
*
*/
public class CheckRealFileTypeUtils {
// private static Logger logger = LoggerFactory.getLogger(CheckRealFileTypeUtils.class);
private CheckRealFileTypeUtils() {
}
private final static Map<String, Object> realFileTypeParams = new HashMap<String, Object>();
static {
realFileTypeParams.put("application/vnd.ms-excel", "excel");
realFileTypeParams.put("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"excel");
realFileTypeParams.put("text/plain", "txt");
realFileTypeParams.put("text/csv","csv");
}
/**
* 传入文件所在路径,并判断该真实文件类型是否存在于系统预定义类型集合中
*
* @param fileName 待判断文件名
* @return
*/
public static boolean checkRealFileTypeToBool(String fileName) {
String realFileType = getRealFileType(fileName);
return checkRealFileTypeByrealFileTypeToBool(realFileType);
}
/**
* 传入真实文件类型,并判断该真实文件类型是否存在于系统预定义类型集合中
*
* @param realFileType 真实文件类型
* @return
*/
public static boolean checkRealFileTypeByrealFileTypeToBool(String realFileType) {
return realFileTypeParams.containsKey(realFileType);
}
/**
* 通过真实文件类型获取在本系统中对应的文件格式.
*
* @param realFileType 真实文件类型
* @return
*/
public static String getRealFileTypeKeyByValue(String realFileType) {
return (String) realFileTypeParams.get(realFileType);
}
/**
* 获取传入文件的真实文件路径.
*
* @param fileName 待判断文件名
* @return
*/
public static String getRealFileType(String fileName) {
InputStream inputStream;
try {
inputStream = new FileInputStream(new File(fileName));
} catch (FileNotFoundException e) {
// logger.error("无法获取文件名为:[" + fileName + "]中的文件内容,因为路径不存在,异常原因:" + e.getMessage());
return null;
}
BufferedInputStream bs = new BufferedInputStream(inputStream);
AutoDetectParser paser = new AutoDetectParser();
Detector detector = paser.getDetector();
Metadata metadata = new Metadata();
metadata.add(Metadata.RESOURCE_NAME_KEY, fileName);
MediaType mediaType;
try {
mediaType = detector.detect(bs, metadata);
} catch (IOException e) {
// logger.error("无法读取文件名为:[" + fileName + "]中的文件内容,异常原因:" + e.getMessage());
return null;
}
return mediaType.toString();
}
}
上述工具类中的getRealFileType方法就是获取文件真实类型的方法,基本上都能够判断准确,不过我说的存在问题也如上面的注释写的一样,如果你将CSV类型的文件后缀名改为TXT类型,则Tika无法识别,后面查阅资料后发现其实CSV和TXT之间本身就是互通的,因此在本人的项目中便没有再去区分。
工具类中还存在 realFileTypeParams这个Map集合,创建它主要的目的是方便调用方能够直接传入文件就进行判断在这个集合里是否存在你业务中需要的那些真实文件类型,各位看官需按照自己的业务添加,才能做到灵活多变。
今天就先写到这里,如有不足之处,欢迎大家指出,我再改正!