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";
    }
相关推荐
努力的小雨5 小时前
还在为调试提示词头疼?一个案例教你轻松上手!
后端
魔都吴所谓5 小时前
【go】语言的匿名变量如何定义与使用
开发语言·后端·golang
陈佬昔没带相机5 小时前
围观前后端对接的 TypeScript 最佳实践,我们缺什么?
前端·后端·api
旋风菠萝6 小时前
JVM易混淆名称
java·jvm·数据库·spring boot·redis·面试
Livingbody7 小时前
大模型微调数据集加载和分析
后端
Livingbody7 小时前
第一次免费使用A800显卡80GB显存微调Ernie大模型
后端
weisian1517 小时前
Java WEB技术-序列化和反序列化认识(SpringBoot的Jackson序列化行为?如何打破序列化过程的驼峰规则?如何解决学序列化循环引用问题?)
java·spring boot
橘子编程7 小时前
SpringMVC核心原理与实战指南
java·spring boot·spring·tomcat·mybatis
Goboy8 小时前
Java 使用 FileOutputStream 写 Excel 文件不落盘?
后端·面试·架构
Goboy8 小时前
讲了八百遍,你还是没有理解CAS
后端·面试·架构