Java的文件类型魔术数字判断,更精确的识别文件

摘要:我们上传文件经常需要判断文件后缀名,直接通过源文件名称有时候会不太准确,所以我们需要通过文件的"魔术数字"来判断,更合理的操作文件。

原理

采用的源码来自于hutool的工具类,推荐使用这个工具类,可以自己在这个工具类上增强。

引入maven

xml 复制代码
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.22</version>
</dependency>

FileTypeUtil

java 复制代码
	/**
	 * 根据文件流的头部信息获得文件类型<br>
	 * 注意此方法会读取头部一些bytes,造成此流接下来读取时缺少部分bytes<br>
	 * 因此如果想复用此流,流需支持{@link InputStream#reset()}方法。
	 * @param in {@link InputStream}
	 * @param isExact 是否精确匹配,如果为false,使用前64个bytes匹配,如果为true,使用前8192bytes匹配
	 * @return 类型,文件的扩展名,未找到为{@code null}
	 * @throws IORuntimeException  读取流引起的异常
	 */
	public static String getType(InputStream in,boolean isExact) throws  IORuntimeException  {
		return isExact
				?getType(IoUtil.readHex8192Upper(in))
				:getType(IoUtil.readHex64Upper(in));
	}

FileMagicNumber

改枚举中定义了常见的文件魔术数字,查询出来就用来对比

  • jpg的文件以ffd8ff开头

测试案例

单元测试

ini 复制代码
@Test
    public void test03(){
        String type = FileTypeUtil.getType(new File("C:\\Users\\lanzhuo\\Pictures\\u=3717268932,170151031&fm=30&app=106&f=JPEG.jpg"));
        FileMagicNumber fileMagicNumber = Arrays.stream(FileMagicNumber.values()).filter(v -> ObjectUtil.equal(v.getExtension(), type)).findFirst().orElse(null);
        System.out.println(type);
        System.out.println(fileMagicNumber.getMimeType());

        String type1 = FileTypeUtil.getType(new File("C:\\Users\\lanzhuo\\Pictures\\检修手册.doc"));
        FileMagicNumber fileMagicNumber1 = Arrays.stream(FileMagicNumber.values()).filter(v -> ObjectUtil.equal(v.getExtension(), type1)).findFirst().orElse(null);
        System.out.println(type1);
        System.out.println(fileMagicNumber1.getMimeType());
    }
  • 结果
bash 复制代码
jpg
image/jpeg
doc
application/msword

基于SpringBoot的使用案例

Springboot的预览文件不能制定文件名,否则会变为下载,切记

java 复制代码
@GetMapping("/download")
    public void download(HttpServletResponse response,String fileName) throws Exception{
        File file = new File(fileUploadProperties.getLocalStorage()+fileName);
        if (!file.exists() || !file.isFile()){
            return;
        }
        InputStream inputStream = Files.newInputStream(Paths.get(fileUploadProperties.getLocalStorage() + fileName));
        response.reset();
        response.setContentType(this.generateDownloadContentType(Files.newInputStream(Paths.get(fileUploadProperties.getLocalStorage() + fileName))));
        response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"),"ISO8859-1"));
        ServletOutputStream outputStream = response.getOutputStream();
        byte[] b = new byte[1024];
        int len;
        try {
            while((len = inputStream.read(b)) > 0) {
                outputStream.write(b, 0, len);
            }
        } finally {
            inputStream.close();
        }
    }

    @GetMapping("/preview")
    public void preview(HttpServletResponse response,String fileName) throws Exception{
        File file = new File(fileUploadProperties.getLocalStorage()+fileName);
        if (!file.exists() || !file.isFile()){
            return;
        }
        InputStream inputStream = Files.newInputStream(Paths.get(fileUploadProperties.getLocalStorage() + fileName));
        response.reset();
        ServletOutputStream outputStream = response.getOutputStream();
        byte[] b = new byte[1024];
        int len;
        try {
            while((len = inputStream.read(b)) > 0) {
                outputStream.write(b, 0, len);
            }
        } finally {
            inputStream.close();
        }
    }

    /**
     * 获取文件类型
     * @param inputStream
     * @return
     * @throws Exception
     */
    private String generateDownloadContentType(InputStream inputStream) throws Exception{
        String type = FileTypeUtil.getType(inputStream);
        inputStream.close();
        FileMagicNumber fileMagicNumber = Arrays.stream(FileMagicNumber.values()).filter(v -> ObjectUtil.equal(v.getExtension(), type)).findFirst().orElse(null);
        return null != fileMagicNumber && null != fileMagicNumber.getMimeType() ? fileMagicNumber.getMimeType() : "application/octet-stream";
    }
相关推荐
姜学迁2 分钟前
Rust-枚举
开发语言·后端·rust
【D'accumulation】40 分钟前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
2401_854391081 小时前
高效开发:SpringBoot网上租赁系统实现细节
java·spring boot·后端
Cikiss1 小时前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
wxin_VXbishe1 小时前
springboot合肥师范学院实习实训管理系统-计算机毕业设计源码31290
java·spring boot·python·spring·servlet·django·php
Cikiss1 小时前
微服务实战——平台属性
java·数据库·后端·微服务
OEC小胖胖1 小时前
Spring Boot + MyBatis 项目中常用注解详解(万字长篇解读)
java·spring boot·后端·spring·mybatis·web
2401_857617622 小时前
SpringBoot校园资料平台:开发与部署指南
java·spring boot·后端
quokka562 小时前
Springboot 整合 logback 日志框架
java·spring boot·logback
计算机学姐2 小时前
基于SpringBoot+Vue的在线投票系统
java·vue.js·spring boot·后端·学习·intellij-idea·mybatis