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";
    }
相关推荐
武昌库里写JAVA12 分钟前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
Q_192849990619 分钟前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
Q_192849990622 分钟前
基于Spring Boot的营销项目系统
spring boot
ZSYP-S1 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
Yuan_o_1 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
程序员一诺2 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
DT辰白2 小时前
如何解决基于 Redis 的网关鉴权导致的 RESTful API 拦截问题?
后端·微服务·架构
路在脚下@2 小时前
Spring Boot @Conditional注解
java·spring boot·spring
thatway19893 小时前
AI-SoC入门:15NPU介绍
后端
陶庵看雪3 小时前
Spring Boot注解总结大全【案例详解,一眼秒懂】
java·spring boot·后端