MIME type 的全称是 Multipurpose Internet Mail Extensions (MIME) ,可以标志一个文件的类型。IANA 的网站上有一个正式的 MIME type 的列表,为什么会有这个列表呢?因为并不是所有的文件类型都有 MIME type,这个MIME type 也不是服务器可以随意设置的,服务器/浏览器两头都要实现相同的标准。基本上只有使用广泛的、性能高的、安全的文件类型才会被加入,因为每加入一个,浏览器厂商、服务器厂商都要去实现,成本比较大;二来风险也比较大,如果压缩比率不高可能会浪费网络带宽,严重的可能带来安全问题。服务器正确的设置 MIME type 也非常重要,否则服务器不会解释资源,比如不会播放视频或音频等。
它的语法是 type/subtype
,中间是 /
隔开,不区分大小写(一般都是小写)。type
表示一个大类,可以是视频、音频、文本等。subtype
表示具体的格式,jpeg
png
等。type
又分成两种,一种是 Discrete(独立) 的,另一种是 multpart
。
Discrete type 分为下面5种:
- text 文本
- image 图片
- audio 音频
- video 视频
- application 一般指二进制数据
如果不指定 subtype
,那对于文本文件默认的就是 text/pain
,二进制数据默认的是 application/octet-stream
。它表示的是“未知类型的文本文件”,并不是指所有的文本文件。举个例子,如果 <link>
标签里面的资源应该是 text/css
,如果使用 text/plain
的话,浏览器并不会把它当做一个 CSS 文件来解释。
application/octet-stream
实际上表示的是“未知的二进制数据”,因为安全考虑,浏览器不会执行它或者尝试解释它。如果 Header Content-Disposition
设置为attachment
,浏览器会弹出“另外为”对话框提示保存。
Multipart 一般由多部分组成,比如 multipart/form-data
一般用于浏览器将 HTML 的表单发给服务器。其中用 Content-type
中定义的 boundary
来分割。每一个 “part” 都是一个实体,对于上传表单的字段,每一个 part 都有 HTTP header Content-Disposition
和 Content-Type
。因为有 boundary
可以分割,所以 Content-Length
会被忽略。比如下面这样的一个表单:
1 2 3 4 5 6 |
<form action="http://localhost:8000/" method="post" enctype="multipart/form-data"> <input type="text" name="myTextField"> <input type="checkbox" name="myCheckBox">Check</input> <input type="file" name="myFile"> <button>Send the file</button> </form> |
将会发出的 HTTP 请求如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Upgrade-Insecure-Requests: 1 Content-Type: multipart/form-data; boundary=---------------------------8721656041911415653955004498 Content-Length: 465 -----------------------------8721656041911415653955004498 Content-Disposition: form-data; name="myTextField" Test -----------------------------8721656041911415653955004498 Content-Disposition: form-data; name="myCheckBox" on -----------------------------8721656041911415653955004498 Content-Disposition: form-data; name="myFile"; filename="test.txt" Content-Type: text/plain Simple file. -----------------------------8721656041911415653955004498-- |
之前对接过一个接口,要求每个请求都要带上证书文件,我就是用的 multipart
发出的。
除了常见的这个,还有另一种叫做的 multipart/byteranges
的 MIME type。介绍这个类型之前,我们先了解一下 HTTP 协议支持的“部分相应内容”。HTTP 的请求可以设置 Range
字段,要求只返回请求部分的 bytes。(要求服务器支持,可以通过 Response 的 Accept-Ranges: bytes
判断是否支持)比如下面这个请求,就只会返回 1K 大小的内容。
1 |
curl http://i.imgur.com/z4d4kWk.jpg -i -H "Range: bytes=0-1023" |
发出的 HTTP 请求如下:
1 2 3 |
GET /z4d4kWk.jpg HTTP/1.1 Host: i.imgur.com Range: bytes=0-1023 |
Response 如下:
1 2 3 4 5 |
HTTP/1.1 206 Partial Content Content-Range: bytes 0-1023/146515 Content-Length: 1024 ... (binary content) |
注意这个 HTTP 响应的状态码是 “206 Partial Content”。表示返回的是部分内容。
由此,可能你已经想到,我可以一次请求多个“部分内容”吗?答案是肯定的,这个时候服务器返回的 Response 中,Header 的 Content-Type
就会是 multipart/byteranges
。其中每一个“部分响应”都会带有 Header Content-Type
和 Content-Range
。比如这个请求:
1 |
curl http://www.example.com -i -H "Range: bytes=0-50, 100-150" |
得到的 Response 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
HTTP/1.1 206 Partial Content Content-Type: multipart/byteranges; boundary=3d6b6a416f9b5 Content-Length: 282 --3d6b6a416f9b5 Content-Type: text/html Content-Range: bytes 0-50/1270 <!doctype html> <html> <head> <title>Example Do --3d6b6a416f9b5 Content-Type: text/html Content-Range: bytes 100-150/1270 eta http-equiv="Content-type" content="text/html; c --3d6b6a416f9b5-- |
关于 Range_requests 的更多内容可以参考 MDN 的有关部分。
最后,如果 MIME type 缺失的话,客户端可能去尝试猜测它的类型。这在不同浏览器的表现是不同的,可能有安全隐患,比如某些被资源被认为是“可执行的”。服务器可以通过设置 X-Content-Type-Options
来禁止客户端进行猜测。
除了设置 MIME type之外,还有两种方法可以表示文件类型:
- 使用文件后缀名。在 Windows 系统中比较流行,但是这只是一种约定,并不是所有的文件的后缀名都是通用的,有意义的。特别是在 Unix 系的系统中,这只不过是“名字的一部分”而已。
- Magic numbers。比如 GIF89 的文件使用
47 49 46 38 39
开头,PNG 使用89 50 4E 47
开头。但并不是所有文件都会有 Magic numbers,所以这种方法也不是100%可靠的。
在实现方面,一般 HTTP 服务器会帮你处理好 MIME type Header 的设置,比如 Nginx 会使用 mime.types
文件判断什么后缀的文件该返回什么样的 MIME type。
参考资料:
Pingback: 我的删库经历 – 爱读书网
Pingback: My Deletion Experience – FENQ