SpringBoot 文件上传 通过Content-Type和文件头判断文件类型

一、关于MIME

MIME的全称是Multipurpose Internet Mail Extensions,即多用途互联网邮件扩展,尽管读起来有些拗口,但大多数人可能都知道,
这是HTTP协议中用来定义文档性质及格式的标准。IETF RFC 6838,对HTTP传输内容类型进行了全面定义。
IANA(互联网号码分配机构)是负责管理所有标准MIME类型的官方机构。可以在这里)找到所有的标准MIME

服务器通过MIME告知响应内容类型,而浏览器则通过MIME类型来确定如何处理文档;
因此为传输内容(文档、图片等)设置正确的MIME非常重要

通常Server会在HTTP响应中设置Content-Type,如下面的响应:

HTTP/1.1 200 OK
Server: Golfe2
Content-Length: 233
Content-Type: application/html
Date: Sun, 28 Dec 2018 02:55:19 GMT

这表示服务端将返回html格式的文档,而同样客户端也可以在HTTP请求中设置Content-Type以告知服务器当前所发送内容的格式。
如下面的请求体:

POST / HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Connection: keep-alive
Content-Type: application/json
Content-Length: 465

这表示客户端会发送application/json格式的数据到服务端,同时应该注意到Accept请求头,这个选项用于告知服务器应该返回什么样的数据格式(由客户端接收并完成解析)。

MIME的格式

type/subtype

这是一个两级的分类,比较容易理解,第一级分类通常包含:

类型 描述
text 普通文本
image 某种图像
audio 某种音频文件
video 某种视频文件
application 应用数据
multi-part 复合内容

而二级类型则非常多,以下是一些常用的MIME:

MIME 描述
audio/wav wave音频流媒体文件
audio/webm webm 音频文件格式
audio/ogg ogg多媒体文件格式的音频文件
audio/mpeg mpeg多媒体文件格式的音频文件
image/gif gif图片
image/jpeg jpeg图片
image/png png图片
image/svg+xml svg矢量图片
application/json json格式
application/xml xml格式
application/xhtml+xml 扩展html格式
application/x-www-form-urlencoded 表单url内容编码
application/octet-stream 二进制格式
application/pdf pdf文档
application/atom+xml atom订阅feed流
multipart/form-data 多文档格式
text/plain 普通文本
text/html html文档
text/css css文件
text/javascript javascript文件
text/markdown markdown文档
video/mpeg mpeg多媒体视频文件
video/quicktime mov多媒体视频文件

MIME Type 与 Content-Type 的关系

首先看看tomcat服务器中默认的web.xml中的描述:

<!-- ===================== Default MIME Type Mappings =================== -->
<!-- When serving static resources, Tomcat will automatically generate    -->
<!-- a "Content-Type" header based on the resource's filename extension, -->
<!-- based on these mappings. Additional mappings can be added here (to -->
<!-- apply to all web applications), or in your own application's web.xml -->
<!-- deployment descriptor.   -->

再看看apache服务器中mime.types的描述:

This file controls what Internet media types are sent to the client for
given file extension(s). Sending the correct media type to the client
is important so they know how to handle the content of the file.
Extra types can either be added here or by using an AddType directive
in your config files. For more information about Internet media types,
please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type registry is at http://www.iana.org/assignments/media-types/.

当web服务器收到静态的资源文件请求时,依据请求文件的后缀名在服务器的MIME配置文件中找到对应的MIME Type,再根据MIME Type设置HTTP Response的Content-Type,然后浏览器根据Content-Type的值处理文件。

也就是说, 文件扩展名=>MIME Type=>Content-Type

通过文件头识别文件

不同的文件类型有不同的文件头, 而文件头部的几个字节被称为Magic Number, 通常用十六进制表示, 可用来判断文件类型.

比如png文件的文件头Magic Number是0x89504E开始的, java class文件Magic Number为Oxcafebabe

我们可以通过判断文件的文件头信息来判断文件的类型, 而且即使改变文件扩展名文件头信息也是不改变的.

通过java代码判断文件类型:

public class FileType {//默认判断文件头前三个字节内容public static int CHECK_BYTES_NUMBER = 3;public final static Map<String, String> FILE_TYPE_MAP = new HashMap<String, String>();private FileType(){}static{getAllFileType(); //初始化文件类型信息}/*** Discription:[getAllFileType,常见文件头信息]*/private static void getAllFileType(){FILE_TYPE_MAP.put("ffd8ffe000104a464946", "jpg"); //JPEG (jpg)FILE_TYPE_MAP.put("89504e470d0a1a0a0000", "png"); //PNG (png)FILE_TYPE_MAP.put("47494638396126026f01", "gif"); //GIF (gif)FILE_TYPE_MAP.put("49492a00227105008037", "tif"); //TIFF (tif)FILE_TYPE_MAP.put("424d228c010000000000", "bmp"); //16色位图(bmp)FILE_TYPE_MAP.put("424d8240090000000000", "bmp"); //24位位图(bmp)FILE_TYPE_MAP.put("424d8e1b030000000000", "bmp"); //256色位图(bmp)FILE_TYPE_MAP.put("41433130313500000000", "dwg"); //CAD (dwg)FILE_TYPE_MAP.put("3c21444f435459504520", "html"); //HTML (html)FILE_TYPE_MAP.put("3c21646f637479706520", "htm"); //HTM (htm)FILE_TYPE_MAP.put("48544d4c207b0d0a0942", "css"); //cssFILE_TYPE_MAP.put("696b2e71623d696b2e71", "js"); //jsFILE_TYPE_MAP.put("7b5c727466315c616e73", "rtf"); //Rich Text Format (rtf)FILE_TYPE_MAP.put("38425053000100000000", "psd"); //Photoshop (psd)FILE_TYPE_MAP.put("46726f6d3a203d3f6762", "eml"); //Email [Outlook Express 6] (eml)FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "doc"); //MS Excel 注意:word、msi 和 excel的文件头一样FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "vsd"); //Visio 绘图FILE_TYPE_MAP.put("5374616E64617264204A", "mdb"); //MS Access (mdb)FILE_TYPE_MAP.put("252150532D41646F6265", "ps");FILE_TYPE_MAP.put("255044462d312e350d0a", "pdf"); //Adobe Acrobat (pdf)FILE_TYPE_MAP.put("2e524d46000000120001", "rmvb"); //rmvb/rm相同FILE_TYPE_MAP.put("464c5601050000000900", "flv"); //flv与f4v相同FILE_TYPE_MAP.put("00000020667479706d70", "mp4");FILE_TYPE_MAP.put("49443303000000002176", "mp3");FILE_TYPE_MAP.put("000001ba210001000180", "mpg"); //FILE_TYPE_MAP.put("3026b2758e66cf11a6d9", "wmv"); //wmv与asf相同FILE_TYPE_MAP.put("52494646e27807005741", "wav"); //Wave (wav)FILE_TYPE_MAP.put("52494646d07d60074156", "avi");FILE_TYPE_MAP.put("4d546864000000060001", "mid"); //MIDI (mid)FILE_TYPE_MAP.put("504b0304140000000800", "zip");FILE_TYPE_MAP.put("526172211a0700cf9073", "rar");FILE_TYPE_MAP.put("235468697320636f6e66", "ini");FILE_TYPE_MAP.put("504b03040a0000000000", "jar");FILE_TYPE_MAP.put("4d5a9000030000000400", "exe");//可执行文件FILE_TYPE_MAP.put("3c25402070616765206c", "jsp");//jsp文件FILE_TYPE_MAP.put("4d616e69666573742d56", "mf");//MF文件FILE_TYPE_MAP.put("3c3f786d6c2076657273", "xml");//xml文件FILE_TYPE_MAP.put("494e5345525420494e54", "sql");//xml文件FILE_TYPE_MAP.put("7061636b616765207765", "java");//java文件FILE_TYPE_MAP.put("406563686f206f66660d", "bat");//bat文件FILE_TYPE_MAP.put("1f8b0800000000000000", "gz");//gz文件FILE_TYPE_MAP.put("6c6f67346a2e726f6f74", "properties");//bat文件FILE_TYPE_MAP.put("cafebabe0000002e0041", "class");//bat文件FILE_TYPE_MAP.put("49545346030000006000", "chm");//bat文件FILE_TYPE_MAP.put("04000000010000001300", "mxp");//bat文件FILE_TYPE_MAP.put("504b0304140006000800", "docx");//docx文件FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "wps");//WPS文字wps、表格et、演示dps都是一样的FILE_TYPE_MAP.put("6431303a637265617465", "torrent");FILE_TYPE_MAP.put("6D6F6F76", "mov"); //Quicktime (mov)FILE_TYPE_MAP.put("FF575043", "wpd"); //WordPerfect (wpd)FILE_TYPE_MAP.put("CFAD12FEC5FD746F", "dbx"); //Outlook Express (dbx)FILE_TYPE_MAP.put("2142444E", "pst"); //Outlook (pst)FILE_TYPE_MAP.put("AC9EBD8F", "qdf"); //Quicken (qdf)FILE_TYPE_MAP.put("E3828596", "pwl"); //Windows Password (pwl)FILE_TYPE_MAP.put("2E7261FD", "ram"); //Real Audio (ram)}/*** 根据制定文件的文件头判断其文件类型* @param filePaht* @return*/public static String getFileType(String filePaht){String res = null;try {FileInputStream is = new FileInputStream(filePaht);getFileType(is);} catch (FileNotFoundException e) {e.printStackTrace();}return res;}public static String getFileType(InputStream in){String res = null;try {byte[] b = new byte[CHECK_BYTES_NUMBER];in.read(b, 0, b.length);String fileCode = bytesToHexString(b);//            System.out.println(fileCode);//这种方法在字典的头代码不够位数的时候可以用但是速度相对慢一点Iterator<String> keyIter = FILE_TYPE_MAP.keySet().iterator();while(keyIter.hasNext()){String key = keyIter.next();if(key.toLowerCase().startsWith(fileCode.toLowerCase()) || fileCode.toLowerCase().startsWith(key.toLowerCase())){res = FILE_TYPE_MAP.get(key);break;}}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return res;}/*** 得到上传文件的文件头* @param src* @return*/public static String bytesToHexString(byte[] src) {StringBuilder stringBuilder = new StringBuilder();if (src == null || src.length <= 0) {return null;}for (int i = 0; i < src.length; i++) {int v = src[i] & 0xFF;String hv = Integer.toHexString(v);if (hv.length() < 2) {stringBuilder.append(0);}stringBuilder.append(hv);}return stringBuilder.toString();}public static int getCheckBytesNumber() {return CHECK_BYTES_NUMBER;}public static void setCheckBytesNumber(int checkBytesNumber) {CHECK_BYTES_NUMBER = checkBytesNumber;}
}

常见文件头表示如下:

255044PDF
526563 EML
D0CF11 PPT
4D5AEE COM
E93B03 COM
4D5A90 EXE
424D3E BMP
49492A TIF
384250 PSD
C5D0D3 EPS
0A0501 PCS
89504E PNG
060500 RAW
000002 TGA
60EA27 ARJ
526172 RAR
504B03 ZIP
495363 CAB
1F9D8C Z
524946 WAV
435753 SWF
3026B2 WMV
3026B2 WMA
2E524D RM
00000F MOV
000077 MOV
000001 MPA
FFFB50 MP3
234558 m3u
3C2144 HTM
FFFE3C XSL
3C3F78 XML
3C3F78 MSC
4C0000 LNK
495453 CHM
805343 scm
D0CF11 XLS
31BE00 WRI
00FFFF MDF
4D4544 MDS
5B436C CCD
00FFFF IMG
FFFFFF SUB
17A150 PCB
2A5052 ECO
526563 PPC
000100 DDB
42494C LDB
2A7665 SCH
2A2420 LIB
434841 FNT
7B5C72 RTF
7B5072 GTD
234445 PRG
000007 PJT
202020 BAS
000002 TAG
4D5A90 dll
4D5A90 OCX
4D5A50 DPL
3F5F03 HLP
4D5A90 OLB
4D5A90 IMM
4D5A90 IME
3F5F03 LHP
C22020 NLS
5B5769 CPX
4D5A16 DRV
5B4144 PBK
24536F PLL
4E4553 NES
87F53E GBC
00FFFF SMD
584245 XBE
005001 XMV
000100 TTF
484802 PDG
000100 TST
414331 dwg
D0CF11 max

另外还有一些重要的文件,没有固定的文件头,如下:

TXT 没固定文件头定义
TMP 没固定文件头定义
INI 没固定文件头定义
BIN 没固定文件头定义
DBF 没固定文件头定义
C 没没固定文件头定义
CPP 没固定文件头定义
H 没固定文件头定义
BAT 没固定文件头定义

还有一些不同的文件有相同的文件头,最典型的就是下面:

4D5A90 EXE
4D5A90 dll
4D5A90 OCX
4D5A90 OLB
4D5A90 IMM
4D5A90 IME

文件上传

当我们需要实现上传文件的时候, 为了安全起见, 我们需要判断上传文件的格式, 防止将病毒木马等有害的文件上传到服务器上.

判断文件类型的三种方式

  • 通过文件后缀名

    这个方法只要修改后缀名就可以了

  • 通过Content-Type判断

    但是Content-Type取决于文件类型, 文件类型取决于文件扩展名, 所以改变了文件扩展名就改变了Content-Type

  • 通过文件头判断文件, 即使文件扩展名改变了文件头也不会改变

文件上传的思路: 先判断Content-Type, Content-Type符合条件的再判断文件头信息

@ResponseBody@GetMapping("validate")public Map<String, String> validate(@Validated({AllFiled.class}) UserInfo userInfo, BindingResult result){//        SpringValidatorAdapter adapter = (SpringValidatorAdapter)result;Map<String, String> map = new HashMap<String, String>();if (result.hasErrors()) {List<ObjectError> list  = result.getAllErrors();for (ObjectError error :list) {FieldError fieldError = (FieldError)error;String defaultMessage = fieldError.getDefaultMessage();String field = fieldError.getField();map.put(field, defaultMessage);}}return map;}
//    consumes = {//        MediaType.MULTIPART_FORM_DATA_VALUE }, produces = MediaType.TEXT_PLAIN_VALUE@PostMapping(value = "file")@ResponseBodypublic String file(@RequestParam("username") String name, MultipartFile file) throws IOException {//获取文件名String fileName = file.getOriginalFilename();//获取表单提交文件使用的字段String partName = file.getName();//判断文件是否为空boolean empty = file.isEmpty();//获取ContentTypeString contentType = file.getContentType();//获取文件直接数Long size = file.getSize();//获取文件所有字节byte[] bytes = file.getBytes();//获取InputStreamInputStream in = file.getInputStream();//根据文件头获取文件类型String type = FileType.getFileType(in);//业务.....StringBuilder builder = new StringBuilder();//存储文件File root = new File("D:/temp");if (!root.isDirectory()) {root.mkdirs();}try {file.transferTo(new File(root, name));return String.format("Upload to %s", fileName);} catch (IllegalStateException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return "Upload Failed";}

SpringBoot 文件上传 通过Content-Type和文件头判断文件类型相关推荐

  1. Spring MVC文件上传示例教程 - 单个和多个文件

    Spring MVC文件上传示例教程 - 单个和多个文件 文件上传是任何Web应用程序中非常常见的任务.我们之前已经看过如何在Servlet和Struts2文件上传中上传文件.今天我们将学习Sprin ...

  2. Spring Boot文件上传及回显(单/多文件)

    一.单文件上传 1.前端页面 <!DOCTYPE html> <html lang="en"> <head><meta charset=& ...

  3. springboot文件上传、下载使用ftp工具将文件上传至服务器

    springboot文件上传.下载使用ftp工具 首先在服务器搭建ftp服务 配置文件(在application.properties中) # Single file max size multipa ...

  4. Js中清空文件上传字段(input type=file )

       表单中type=file字段的value属性无法由js来附值,一但选中某个文件后,如果用户不手动去清空那么这个值将保留,提交表单时对应文件也会被提交上去.当然在服务器上会再次验证,不过为了避免上 ...

  5. java 文件上传 jar_JavaWeb 之 使用 commons-fileupload.jar 实现文件上传

    一.文件上传 文件的上传和下载,是非常常见的功能,在很多的系统中,或软件中都经常使用文件的上传和下载 文件的上传主要分为下面几步: 1.前台页面需要一个 form 标签,里面的 method 为 po ...

  6. Winform中实现FTP客户端并定时扫描指定路径下文件上传到FTP服务端然后删除文件

    场景 Windows10上怎样开启FTP服务: Windows10上怎样开启FTP服务_BADAO_LIUMANG_QIZHI的博客-CSDN博客 上面在Windows上搭建FTP服务器之后,会接收客 ...

  7. java awt文件上传_springMVC实现前台带进度条文件上传的示例代码

    项目框架采用spring+hibernate+springMVC如果上传文件不想使用flash那么你可以采用HTML5;截图前段模块是bootstarp框架;不废话直接来代码;spring-mvc配置 ...

  8. 使用 Apache的文件上传组件(common-fileupload)来实现文件的上传

    文章目录 一.前言/先导 二.步骤 三.源码 四.测试结果 五.文件的下载 一.前言/先导 maven的依赖: 注意:common-fileupload,它需要依赖于 commons-io组件: &l ...

  9. 任意文件上传mysql_[代码审计]XiaoCms(后台任意文件上传至getshell,任意目录删除,会话固定漏洞)...

    0x00 前言 这段时间就一直在搞代码审计了.针对自己的审计方法做一下总结,记录一下步骤. 审计没他,基础要牢,思路要清晰,姿势要多且正. 下面是自己审计的步骤,正在逐步调整,寻求效率最高. 0x01 ...

最新文章

  1. shell脚本初级教学(从基本脚本开始学起)
  2. ubuntu14.04系统扩容的方法
  3. 原型模式Prototype,constructor,__proto__详解
  4. 济南计算机中考分数,【更新版】2017济南各初中中考成绩汇总!
  5. ASP.NET MVC开发中常见异常及解决方案
  6. Python thread
  7. JAAS:灵活的Java安全机制[转]
  8. Java类类getPackage()方法及示例
  9. Liskon替换原则
  10. 创建激光雷达(rplidar)的串口别名
  11. java多进程_Java中创建多进程
  12. 根据导出的查询结果拼接字符串,生成sql语句并保存到txt文件中
  13. 文件服务器软件_使用Home FTP Server在电脑之间传文件
  14. php zscan,PHP redis SCAN、SSCAN、ZSCAN、HSCAN 的使用, pipe 快速redis插入数据
  15. 互联网摸鱼日报(2022-10-17)
  16. 广外男生病毒代码剖析
  17. 三元平衡系统与反物质研究
  18. 用数学规划的方式求解优化问题
  19. OpenCV如何进行图像的平滑和锐化处理?
  20. 20220312纪中集训总结

热门文章

  1. 商务网站建设与维护【1】
  2. linux认证和红帽认证,linux认证之红帽认证:RedHatLinux新手入门(5
  3. Matlab + Adobe illustrator科研作图
  4. android开发笔记之Google Analytics
  5. 酷欧天气 java.lang.RuntimeException: Unable to start activity ComponentInfo,程序无法运行
  6. 智深正使得活泛 天空软件下载
  7. 下一代防火墙组网简介
  8. C# Windows Service入门
  9. 计算机多出一个盘无法读,电脑有一个盘读不出来了
  10. 关于UNITY中System.Drawing引用失败的处理方法