【黑马程序员】Java如何判断文件的真实类型
在我们实现一些需求的时候,有时候需要让用户上传文件,例如做论坛、社交类的应用和服务。这时候可能就会出现一个需求:上传固定类型、固定大小的文件,也就是说,只允许用户上传图片或者文本、压缩包等。其他的格式禁止上传,以防止用户恶意操作。文件的大小非常容易判断,主要是文件类型如何确定。
        有一种方式是按照扩展名去判断,例如图片就是.jpg.png等,但是这种方式不准确,因为如果真是恶意上传,exe文件也可以修改为xxx.jpg上传成功。所以,必须要用一种方式获取文件的真实类型。即使文件后缀名修改了,也能出来它的原形。
        这个地方我们需要先了解一些基本知识,就是所有的文件的开头都有一段头信息,用来承担一定的任务,而每个不同类型的文件的头信息都是不同的,所以我们可以按照这种方式来进行判断。
        首先,我们需要获取到文件头里这几个字节的数据,很简单,使用输入流来读取即可。代码如下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public static String getFileHeader(String filePath) {
                FileInputStream is = null;
                String value = null;
                try {
                        is = new FileInputStream(filePath);
                        byte[] b = new byte[4];
                        is.read(b, 0, b.length);
                        value = bytesToHexString(b);
                } catch (Exception e) {
                } finally {
                        if (null != is) {
                                try {
                                        is.close();
                                } catch (IOException e) {
                                }
                        }
                }
                return value;
        }

      这个地方很容易理解,就是把文件的前4byte取出来了,但是取出来的数据我们需要转换为16进制的字符串来表示,才能判断出来究竟是什么内容。接下来,我们就把数据转换一下。Integer里有一个API可以直接输出16进制的字符串,所以我们可以先把byte转换为integer来表示。由于byte是一个字节,而integer4个字节,所以不足的部分用0填充,即用byte0xFF做一次与运算,完成byteinteger的转换,然后把16进制的数据输出,字符串转换为大写以便后面使用。
01
02
03
04
05
06
07
08
09
10
培训机构出来的程序员真的强吗11
12
13
14
15
16
private static String bytesToHexString(byte[] src) {
                StringBuilder builder = new StringBuilder();
                if (src == null || src.length <= 0) {
                        return null;
                }
                String hv;
                for (int i = 0; i < src.length; i++) {
                        // 以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式,并转换为大写
                        hv = HexString(src[i] & 0xFF).toUpperCase();
                        if (hv.length() < 2) {
                                builder.append(0);
                        }
                        builder.append(hv);
                }
                return String();
        }

        得到了头信息里的数据之后,我们拿到的其实是一堆看上去像乱码的字符串,并不能判断出来究竟是什么类型的文件,所以我们需要搜集常见的文件类型的头信息都是什么,然后用映射关系表来查。我这里准备了一些常用的文件的头信息,已经封装到了一个map里,可以参考。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static final HashMap<String, String> mFileTypes = new HashMap<String, String>();
        static {
                // images
                mFileTypes.put("FFD8FF", "jpg");
                mFileTypes.put("89504E47", "png");
                mFileTypes.put("47494638", "gif");
                mFileTypes.put("49492A00", "tif");
                mFileTypes.put("424D", "bmp");
                //
                mFileTypes.put("41433130", "dwg"); // CAD
                mFileTypes.put("38425053", "psd");
                mFileTypes.put("7B5C727466", "rtf"); // 日记本
                mFileTypes.put("3C3F786D6C", "xml");
                mFileTypes.put("68746D6C3E", "html");
                mFileTypes.put("44656C69766572792D646174653A", "eml"); // 邮件
                mFileTypes.put("D0CF11E0", "doc");
                mFileTypes.put("D0CF11E0", "xls");// excel2003版本文件
                mFileTypes.put("5374616E64617264204A", "mdb");
                mFileTypes.put("252150532D41646F6265", "ps");
                mFileTypes.put("255044462D312E", "pdf");
                mFileTypes.put("504B0304", "docx");
                mFileTypes.put("504B0304", "xlsx");// excel2007以上版本文件
                mFileTypes.put("52617221", "rar");
                mFileTypes.put("57415645", "wav");
                mFileTypes.put("41564920", "avi");
                mFileTypes.put("2E524D46", "rm");
                mFileTypes.put("000001BA", "mpg");
                mFileTypes.put("000001B3", "mpg");
                mFileTypes.put("6D6F6F76", "mov");
                mFileTypes.put("3026B2758E66CF11", "asf");
                mFileTypes.put("4D546864", "mid");
                mFileTypes.put("1F8B08", "gz");
        }

      这些都是一些固定的数据,我们现在有了mapkey(其实就是上面我们最终获取到的字符串),直接去map里查value就是文件的实际类型了。我们把一个png文件修改为1.avi试一下。打印结果是OK的。

      这样,我们就可以把上面的代码整理封装为一个工具类来使用了,以后再处理文件上传的时候,就可以把一些恶意的操作屏蔽掉了。整体代码附到附件里自取吧。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。