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";
    }
相关推荐
Long_poem18 分钟前
【自学笔记】Spring Boot框架技术基础知识点总览-持续更新
spring boot·笔记·后端
楠枬1 小时前
网页五子棋——对战后端
java·开发语言·spring boot·websocket·spring
hong_zc1 小时前
SpringBoot 配置文件
java·spring boot·后端
神马都会亿点点的毛毛张1 小时前
【Docker教程】万字长文详解Docker命令
java·运维·后端·docker·容器
朗迹 - 张伟2 小时前
Golang连接使用SqlCipher
开发语言·后端·golang
m0_748257462 小时前
创建一个简单的spring boot+vue前后端分离项目
vue.js·spring boot·后端
m0_748234902 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
神马都会亿点点的毛毛张2 小时前
【SpringBoot教程】SpringBoot整合Caffeine本地缓存及Spring Cache注解的使用
java·spring boot·后端·spring·缓存·caffeine
zhyhgx3 小时前
【Spring】Spring配置文件
java·服务器·spring boot·后端·spring·配置文件