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";
    }
相关推荐
没有黑科技7 分钟前
基于web的音乐网站(Java+SpringBoot+Mysql)
java·前端·spring boot
计算机毕设孵化场7 分钟前
计算机毕设-基于springboot的多彩吉安红色旅游网站的设计与实现(附源码+lw+ppt+开题报告)
vue.js·spring boot·后端·计算机外设·课程设计·计算机毕设论文·多彩吉安红色旅游网站
爪哇学长9 分钟前
解锁API的无限潜力:RESTful、SOAP、GraphQL和Webhooks的应用前景
java·开发语言·后端·restful·graphql
战神刘玉栋33 分钟前
《SpringBoot、Vue 组装exe与套壳保姆级教学》
vue.js·spring boot·后端
码到成功>_<2 小时前
Spring Boot实现License生成和校验
数据库·spring boot·后端
Ztiddler3 小时前
【npm设置代理-解决npm网络连接error network失败问题】
前端·后端·npm·node.js·vue
货拉拉技术3 小时前
多元消息融合分发平台
javascript·后端·架构
醒过来摸鱼3 小时前
【Golang】协程
开发语言·后端·golang
谷大羽3 小时前
Kafka Stream实战教程
spring boot·后端·中间件·kafka·stream
2401_857636393 小时前
实验室管理平台:Spring Boot技术构建
java·spring boot·后端