你真的知道网页上传文件背后的原理吗?

今年第三季度工作上完成了一个比较有意思的项目,类似于外包的性质,主要任务就是提供一大堆API,其中一个API是上传附件,完成开发后,对方的程序员问我,这个API怎么调用,当时我就愣住了,因为自己也没想过这个问题,一般情况下,我就是用 Curl 命令行或 Postman 测试API的。

针对文件上传,我使用 Curl 测试,比如:

# 使用@引用一个文件
$ curl -F"param=value" -F"file=@/path/file.png" http://localhost/api.php

如果使用Postman测试,如下图:

图1

注意观察form-data和File标签。

看上去是不是很简单,现在换个角度,你想以代码的方式上传文件API,怎么办?也非常简单,很多开发语言有很多现成的库,比如PHP通过Curl库上传文件非常容易。再深入想一想,如果不使用这些库,怎么上传文件?可能会难倒很多人,所以这篇文章简单讲讲文件上传的原理,其实就是根据HTTP协议的定义,封装一个HTTP消息体。

MIME

首先必须先讲下MIME(Multipurpose Internet Mail Extensions),它并不是HTTP协议的一部分,就像我们每个人都是独一无二的,有自己的属性,互联网上每个资源也有属性,比如有些资源是图片,有些是视频,有些是HTML页面,MIME规定了每种资源的类型,这个类型不是随便定义的,由IANA负责登记和维护。

说的有点难理解,比如你看到一个URL地址,http://localhost/image.png,我们其实并不是通过.png后缀判断资源类型的,而是通过MIME来获知该资源类型的,这个图片的MIME可能就是image/png(至于客户端如何知晓资源的MIME类型,后面会讲),现在是不是有了点感性的认识了。

MIME类型结构如下:

type/subtype

type相当于某些类型的集合,而subtype相当于子类型。以image/png为例,image表示图片类型集合,png表示某种类型图片。

让我们看几个比较重要的MIME类型:

text/plain
text/html
application/octet-stream
multipart/form-data

其实本篇文章的主角就是multipart/form-data,再等一等,先别着急,再一次说说MIME,从它的英文全称来看,它和mail有关系,是由mail应用定义而来的,一封邮件由多种资源组成,为了将不同类型的资源组成在邮件中,MIME产生了。随着互联网Web的发展,MIME的作用越来越多,扩展也越来越多,MIME概念也逐步移到了Web。

Content Type

现在我们定义了每种资源的MIME类型,那么客户端如何知晓每种资源的MIME类型呢?这时候就要使用Content-Type HTTP Header 头了,比如我们请求一个资源,Web服务器在发送资源的时候,发送了“content-type:image/png” Header 头,这样客户端就知道该资源是一个png图片了。

如果客户端发送了一个 “Content-Type: multipart/form-data;”,代表客户端要上传一个附件。

也就是说 Content-Type 后面的值就是一个 MIME 类型,聪明的同学也猜到了,上传附件和 multipart/form-data MIME 类型有关,确实是!

multipart/form-data

multipart/form-data 这个MIME类型并不是标准的MIME类型,而是因为Web的需要扩展而来的,我们在开发网页的时候为了上传一个文件,会输入以下的HTML标签:

<form action="upload.php" method="post" enctype="multipart/form-data">
    Select image to upload:
    <input type="file" name="fileToUpload" id="fileToUpload">
    <input type="submit" value="Upload Image" name="submit">
</form>

关于HTML表单上传可以参考 https://www.w3.org/TR/html5/sec-forms.html#multipart-form-data 或 RFC 1867(Form-based File Upload in HTML,该RFC已经废弃了)。

那么multipart/form-data表示什么呢?multipart互联网上的混合资源,就是资源由多种元素组成,form-data表示可以使用HTML Forms 和 POST 方法上传文件,具体的定义可以参考RFC 7578。

multipart/form-data结构

说了那么多,从HTTP协议的角度,最后看下文件上传的HTTP消息体,使用Postman也容易看出,如下:

POST /api.php HTTP/1.1
Host: localhst
Cache-Control: no-cache
Content-Type: multipart/form-data; boundary=----FormBoundary

------FormBoundary
Content-Disposition: form-data; name="file"; filename="file.png"
Content-Type: image/png

<图片二进制内容>
------FormBoundary
Content-Disposition: form-data; name="param1"

value1
------FormBoundary
Content-Disposition: form-data; name="param2"

value2
------FormBoundary--

消息体什么意思呢,如果你自行想使用代码实现文件上传,要根据定义自行封装HTTP消息,接下去我们简单描述一下。

Content-Type: multipart/form-data; boundary=----FormBoundary 表示要上传附件,其中boundary表示分隔符,如果要上传多个表单项,就要使用boundary分割,每个表单项由------FormBoundary开始,以------FormBoundary结尾。每一个表单项又由Content-Type和Content-Disposition组成。

------FormBoundary
Content-Disposition: form-data; name="param1"

value1
------FormBoundary

表示普通的一个表单元素,最重要的是理解 Content-Disposition HTTP 消息头,其中第一个参数总是固定不变的form-data,name表示表单元素属性名,回车换行符后面的内容就是元素的值。

接下去重点描述和文件有关的:

------FormBoundary
Content-Disposition: form-data; name="file"; filename="file.png"
Content-Type: image/png

<图片二进制内容>
------FormBoundary

其中多了一个filename参数,表示文件名,Content-Type 告诉服务器这是一个图片,内容就是图片的二进制数据。

其实Content-Disposition这个HTTP header头用途也很广泛,在本文就不重点描述了。

其实要自行封装文件上传,最好的办法就是用自己熟悉的开发语言实现一下,这样印象才更深刻,希望这篇文章对你有用。

我最近写了一本新书《深入浅出HTTPS:从原理到实战》,本书github地址是 https://github.com/ywdblog/httpsbook,大家可以一起讨论本书。本书豆瓣地址 https://book.douban.com/subject/30250772/,如果你读了本书,还请在豆瓣写个评论。或者关注我的公众号(ID:yudadanwx,虞大胆的叽叽喳喳),我会分享一些原创文章(这篇文章有彩蛋哦,可能只有我自己才能知道了:))。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容

  • 一直到今天我和大林都没谈到一年。但我们却像在一起了很久很久。因为上一段感情的原因,我倒现在还是没有安全感,我也相信...
    曾曾的麻麻阅读 346评论 0 0
  • 上周计划@完成情况 1、阅读《 乖 摸摸头 》,完本✔️ 2、学一首新口琴曲《Oh!susanna》✖️ 3、《金...
    幸运风铃阅读 166评论 0 0
  • 这个年过的温暖而又踏实,我每天穿着林懿轩送的羽绒服,这个冬天很温暖。 转眼间到了初八,韩旭和林懿轩还有莹莹在琪...
    佰莫思韩阅读 1,301评论 0 0
  • 德国哲学家叔本华说:“针对别人的行为动怒,就跟向一块横在我们前进路上的石头大发脾气同等的愚蠢。”
    梦梦一一阅读 158评论 0 0