JAVA开发小技巧--读取文件魔数来识别文件类型

烟雨 2年前 (2023-08-24) 阅读数 32092 #Java
文章标签 Java

我们在开发应用软件的时候一定会涉及到文件上传的功能,并且我们还要对用户上传文件的合法性进行校验。在文件校验的时候,我们通常会通过文件的后缀名去校验该文件是否合法,但是这样校验就会有一个弊端:如果用户上传的文件是改过文件名后缀的文件该怎么办呢 ?(比如某个上传接口只允许上传图片,那么如果我把一个 .txt 文件的后缀改成 .jpg ,那么就可以绕过文件的后缀名校验方法 )俗话说“繁琐问题必有猥琐解法”,那么今天就给各位小伙伴介绍另外一种文件校验方式 —— 通过文件魔数值进行校验。

魔数这个词在不同领域代表不同的含义。在计算机领域,魔数有两个含义,一指用来判断文件类型的魔数;二指程序代码中的魔数,也称魔法值。

我们今天所说的魔数就是表示不同文件类型的魔术数字,它指定是文件的最开头的几个用于唯一区别其它文件类型的字节,有了这些魔术数字,我们就可以很方便的区别不同的文件,这也使得编程变得更加容易,减少了我们用于区别一个文件的文件类型所要花费的时间 。关于文件校验也没有什么需要过多阐述的东西,下面就直接上代码

/**
 * 利用文件魔数值判断文件类型
 */
public class FileMagicUtils {
    /**
     * 缓存文件魔数值
     */
    public static final HashMap<String, String> mFileTypes = new HashMap<>();

    static {
        mFileTypes.put("jpg", "FFD8FFE0");
        mFileTypes.put("png", "89504E47");
        mFileTypes.put("gif", "47494638");
        mFileTypes.put("tif", "49492A00");
        mFileTypes.put("bmp", "424D");
        mFileTypes.put("psd", "38425053");
        mFileTypes.put("xml", "3C3F786D6C");
        mFileTypes.put("html", "68746D6C3E");
        mFileTypes.put("doc", "D0CF11E0");
        mFileTypes.put("mdb", "5374616E64617264204A");
        mFileTypes.put("pdf", "255044462D312E");
        mFileTypes.put("docx", "504B0304");
        mFileTypes.put("rar", "52617221");
        mFileTypes.put("avi", "41564920");
    }

    /**
     * 获取文件魔数值
     * @param file 对应文件
     * @return 16进制魔数值
     */
    private static String getMagicNumber(File file) {
        byte[] bytes = new byte[20];
        try (FileInputStream inputStream = new FileInputStream(file)) {
            inputStream.read(bytes, 0, 20);
            StringBuilder sb = new StringBuilder();
            for (byte b : bytes) {
                // b & 0xFF转16禁止。
                sb.append(Integer.toHexString(b & 0xFF));
            }
            return sb.toString().toUpperCase();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 判断单个文件的后缀名
     * @param file 文件
     * @param fileSuffix 需要判断的文件后缀名
     */
    public static boolean isAllowedExtension(File file, String fileSuffix){
        String magicNumber = getMagicNumber(file);
        System.out.println(magicNumber);
        return magicNumber.startsWith(mFileTypes.get(fileSuffix));
    }

    /**
     * 判断单个文件的后缀名是否在一个范围
     * @param file 文件
     * @param fileSuffixs 需要判断的文件后缀名
     */
    public static boolean isAllowedExtension(File file, String... fileSuffixs){
        String magicNumber = getMagicNumber(file);
        System.out.println(magicNumber);
        boolean temp = false;
        for (String fileSuffix : fileSuffixs) {
            if(magicNumber.startsWith(mFileTypes.get(fileSuffix))){
                temp = true;
            }
        }
        return temp;
    }

    public static void main(String[] args) {
        System.out.println(isAllowedExtension(new File("D:\\图片1.png"), "png", "jpg"));
    }
}

image.png

运行结果

image.png

方法很简单,没有什么复杂的逻辑,唯一需要注意的就是:代码中罗列出的文件魔数值都是一些常用的文件所对应的魔数值,如果有特殊的文件需要校验的话,可以先跑一次校验工具,获取到对应的魔数值以后,将魔数值加到 HashMap 中就可以正常使用。

版权声明

非特殊说明,本文由Zender原创或收集发布,欢迎转载。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

作者文章
热门