如何判断一个文件的类型

在文章应用安全系列之二十四:文件上传_jimmyleeee的博客-CSDN博客描述了上传文件时需要判断文件的几个方面,其中一个就是文件的类型,当然,如果要根据文件的后缀判断,也能满足大部分需求,如果确实需要判断文件的类型,或者某些时刻文件没有扩展名时,就会遇到麻烦。

文件类型检测分为两种:

  • 文件名检测 - 简单地根据文件后缀名判断文件类型。
  • 魔术字检测 - 有些文件格式会将文件最开始的几个字节设置会特定的模式,通过这些特殊的字节模式,可以判断文件类型。

Java提供了集中方法来判断一个文件的类型,其中比较常用的方法是:Files.probeContentType,但是,它有一个缺点就是,当文件没有扩展名时,返回的结果是null,下面是一个使用这个方法的代码:

public class TestApp {

    public static String identifyFileType(final String fileName) throws IOException
    {
        final File file = new File(fileName);
        return Files.probeContentType(file.toPath());
    }

	public static void main(String[] args) {

		String fileNames[] = {"F:\\filetype\\1.pdf",
				"F:\\filetype\\2.zip",
				"F:\\filetype\\3.docx",
				"F:\\filetype\\4.xls",
				"F:\\filetype\\5.jpg",
				"F:\\filetype\\6.csv",
				"F:\\filetype\\7.txt",
				"F:\\filetype\\8.PNG",
				"F:\\filetype\\9.mp4",
                "F:\\filetype\\10.xml",
                "F:\\filetype\\11.xml",
				"F:\\filetype\\noextpng",
				"F:\\filetype\\csvnoext",
				"F:\\filetype\\mp4noext",
				"F:\\filetype\\pngwithtxt.txt",
			};

		// Test Files.probeContentType
		System.out.println("Test Files.probeContentType Begin");
		try {
			for (String fileName : fileNames) {

				System.out.println(identifyFileType(fileName));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Test Files.probeContentType End");

	}
}

打印结果如下:

Test Files.probeContentType Begin
application/pdf
application/x-zip-compressed
application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/vnd.ms-excel
image/jpeg
application/vnd.ms-excel
text/plain
image/png
video/mp4
text/xml
application/json
null
null
null
text/plain
Test Files.probeContentType End

可以看到,Files.probeContentType是根据扩展名来判断文件类型的,没有扩展名时,返回的是null,当把一个图片改成txt结尾,返回的类型和txt的类型一样。说明如果要根据Files.probeContentType准确判断一个文件的类型是不准确的。

Java还提供了其他几个方法获取文件的类型:

类与方法名说明
MimetypesFileTypeMap::getContentType
只有jpg和txt返回正常,其他全部返回application/octet-stream
URLConnection::getContentType
和Files.probeContentType类似。不同的是,它识别的XML文件的类型是application/xml,而Files.probeContentType识别的XML是text/xml.
URLConnection.guessContentTypeFromName
测试了除了pdf、zip、jpg、txt、png、xml返回的值正常,其它全部返回null

综上所述,目前Java提供的几种方法都不能很好地准确地判断文件的类型,不过,幸运的是Apache提供了Tika,下面是使用默认的Tika的功能的代码:

public static String getFileTypeByDefaultTika(final String fileName)
{
	Tika defaultTika = new Tika();
	String fileType;
	try
	{
		final File file = new File(fileName);
		fileType = defaultTika.detect(file);
	}
	catch (IOException ioEx)
	{
		fileType = "Unknown";
	}
	return fileType;
}

使用本文开头的示例代码打印信息如下:

Test DefaultTikaForFile Begin
application/pdf
application/zip
application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/vnd.ms-excel
image/jpeg
text/csv
text/plain
image/png
video/mp4
application/xml
application/json
image/png
text/plain
video/quicktime
image/png
Test DefaultTikaForFile End

可以看到,所有的文件类型都识别出来了,没有扩展名的文件也是别出来,最为重要的是,我把图片改成txt后缀,它也是别出来这是一个png。

Tika还提供了其他实现,下面表格是将集中的实现结果的比较:

Tika实现方式示例代码说明
默认Tika defaultTika = new Tika();根据上述代码打印结果,完全符合预期,效果最好;
MIMETika mimeTika = new Tika(new MimeTypes());除了txt文件正常输出text/plain,其他全部是application/octet-stream;
TypeDetectorTika typeTika = new Tika(new TypeDetector());全部输出为application/octet-stream;

综上所述,如果需要比较准确地判断一个文件到底是什么类型,不管扩展名是什么,需要使用Tika的默认实现方式;如果只需要根据扩展名判断文件类型就够了,你可以使用Files.probeContentType和URLConnection::getContentType。

关于PHP也有几种获取文件类型的方法,总结如下表:

类与方法名说明
pathinfo($file, PATHINFO_EXTENSION)仅仅是根据文件的扩展名返回文件类型
$_FILES[‘uploadfile’][‘type’]仅仅是根据文件的扩展名返回文件类型
finfo_file(
    resource $finfo,
    string $file_name = null,
    int $options = FILEINFO_NONE,
    resource $context = null
): string
本函数用来获取一个文件的信息。该方法即便是原文件被改过后缀,已然可以读到原文件类型。

总之,在处理文件上传时需要谨记需要做好输入验证,不管是文件名、大小、类型,还是内容等,只有做好了防范,才能预防由文件上传带来的攻击。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值