SpringBoot中 判断上传的文件类型 MultipartFile file

本文介绍了在项目中如何确保上传文件的安全性,特别是针对Excel文件的验证。首先,通过检查文件的后缀名,然后利用Hutool的FileTypeUtil工具类读取文件头部字节来准确判断文件类型,防止恶意用户篡改后缀名进行攻击。此外,还详细解释了FileTypeUtil的工作原理和局限性,并展示了自定义文件类型的例子。对于.zip类型的特殊处理是因为xlsx和docx本质上是ZIP格式的XML文件。
摘要由CSDN通过智能技术生成

最新项目中有遇到 对于上传文件类型的判断   我们需要上传的文件是 excel类型的  

原来我的做法很简单 就是 获取文件名然后去判断 后缀名是否是 .xls 或者是 .xlsx 类型  

        //判断文件类型是否正确
        String originalFilename = file.getOriginalFilename();
        String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
        if (!".xls".equalsIgnoreCase(fileType) && !".xlsx".equalsIgnoreCase(fileType)) {
            return BaseResultUtil.resultError("文件格式不对");
        }

但是这种判断是不严谨的  假如有人恶意攻击   将恶心文件的后缀名改成 xls/xlsx  就能轻易的进行next....所有需要进一步的判断  

下面我们可以借助 Hutool工具类的 FileTypeUtil 来实现  这个的原理就是读取文件首部几个二进制位来判断常用的文件类型  

废话不多说 先摆上我的最终实现    1、先判断后缀名  2、 然后通过 FileTypeUtil 来判断 符合后缀名的文件是否是 对于的文件类型  

    private static final String FILE_ZIP="zip";
    private static final String FILE_XLS=".xls";
    private static final String FILE_XLSX=".xlsx";

    public boolean checkFileType(MultipartFile file)  {
        String filename = file.getOriginalFilename();
        String fileType = filename.substring(filename.lastIndexOf("."));
        //先判断后缀名
        if (FILE_XLS.equalsIgnoreCase(fileType) || FILE_XLSX.equalsIgnoreCase(fileType)) {
            String type ;
            try {
                type = FileTypeUtil.getType(file.getInputStream());
                //根据首部字节判断文件类型
                if (FILE_ZIP.contains(type)||FILE_XLS.contains(type)){
                    return true;
                }
            } catch (Exception e) {
                log.error("上传工作状态excel失败", e);
             
            }

        }
        return false;
    }

FileTypeUtil文件的简单介绍 ----参考Hutool 工具类的官方文档  

https://hutool.cn/docs/#/core/IO/%E6%96%87%E4%BB%B6%E7%B1%BB%E5%9E%8B%E5%88%A4%E6%96%AD-FileTypeUtil

为什么 会用到 zip 类型 下面也有介绍   xlsx、docx本质上是各种XML打包为zip的结果,因此会被识别为zip格式。

 

使用

这个工具类使用非常简单,通过调用FileTypeUtil.getType即可判断,这个方法同时提供众多的重载方法,用于读取不同的文件和流。

File file = FileUtil.file("d:/test.jpg");
String type = FileTypeUtil.getType(file);
//输出 jpg则说明确实为jpg文件
Console.log(type);
Copy to clipboardErrorCopied

原理和局限性

这个类是通过读取文件流中前N个byte值来判断文件类型,在类中我们通过Map形式将常用的文件类型做了映射,这些映射都是网络上搜集而来。也就是说,我们只能识别有限的几种文件类型。但是这些类型已经涵盖了常用的图片、音频、视频、Office文档类型,可以应对大部分的使用场景。

对于某些文本格式的文件我们并不能通过首部byte判断其类型,比如JSON,这类文件本质上是文本文件,我们应该读取其文本内容,通过其语法判断类型。

自定义类型

为了提高FileTypeUtil的扩展性,我们通过putFileType方法可以自定义文件类型。

FileTypeUtil.putFileType("ffd8ffe000104a464946", "new_jpg");Copy to clipboardErrorCopied

第一个参数是文件流的前N个byte的16进制表示,我们可以读取自定义文件查看,选取一定长度即可(长度越长越精确),第二个参数就是文件类型,然后使用FileTypeUtil.getType即可。

注意 xlsx、docx本质上是各种XML打包为zip的结果,因此会被识别为zip格式。

部分源码 

// MS Excel 注意:word、msi 和 excel的文件头一样
fileTypeMap.put("d0cf11e0a1b11ae10", "xls");
fileTypeMap.put("504B0304", "zip");




/**
 * 根据文件流的头部信息获得文件类型
 *
 * <pre>
 *     1、无法识别类型默认按照扩展名识别
 *     2、xls、doc、msi头信息无法区分,按照扩展名区分
 *     3、zip可能为docx、xlsx、pptx、jar、war头信息无法区分,按照扩展名区分
 * </pre>
 *
 * @param file 文件 {@link File}
 * @return 类型,文件的扩展名,未找到为<code>null</code>
 * @throws IORuntimeException 读取文件引起的异常
 */
public static String getType(File file) throws IORuntimeException {
   String typeName;
   FileInputStream in = null;
   try {
      in = IoUtil.toStream(file);
      typeName = getType(in);
   } finally {
      IoUtil.close(in);
   }

   if (null == typeName) {
      // 未成功识别类型,扩展名辅助识别
      typeName = FileUtil.extName(file);
   } else if ("xls".equals(typeName)) {
      // xls、doc、msi的头一样,使用扩展名辅助判断
      final String extName = FileUtil.extName(file);
      if ("doc".equalsIgnoreCase(extName)) {
         typeName = "doc";
      } else if ("msi".equalsIgnoreCase(extName)) {
         typeName = "msi";
      }
   } else if ("zip".equals(typeName)) {
      // zip可能为docx、xlsx、pptx、jar、war等格式,扩展名辅助判断
      final String extName = FileUtil.extName(file);
      if ("docx".equalsIgnoreCase(extName)) {
         typeName = "docx";
      } else if ("xlsx".equalsIgnoreCase(extName)) {
         typeName = "xlsx";
      } else if ("pptx".equalsIgnoreCase(extName)) {
         typeName = "pptx";
      } else if ("jar".equalsIgnoreCase(extName)) {
         typeName = "jar";
      } else if ("war".equalsIgnoreCase(extName)) {
         typeName = "war";
      }
   }
   return typeName;
}

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值