知行学院总结 | 前端快速入门——文件上传流程分析及代码实现
知行学院是青云QingCloud 为广大 CIO、架构师、开发者、运维工程师、技术爱好者提供的一个云计算、大数据、容器等技术的最佳分享与实践平台。其中包括线上技术公开课、云产品解析、线下实践课堂、行业沙龙等众多知识分享形式。
本次我们邀请了青云QingCloud 前端开发工程师段国伟,带来《前端快速入门——文件上传流程分析及代码实现》。
<iframe class="video_iframe" data-vidtype="1" data-cover="http%3A%2F%2Fshp.qpic.cn%2Fqqvideo_ori%2F0%2Fa0814y3uldj_496_280%2F0" allowfullscreen="" frameborder="0" style="z-index: 1; display: none; width: 368px !important; height: 207px !important;" data-ratio="1.7777777777777777" data-w="864" data-src="http://v.qq.com/iframe/player.html?vid=a0814y3uldj&width=368&height=207&auto=0" width="368" height="207" data-vh="207" data-vw="368"></iframe>
正文:
上节课为大家介绍了网盘的技术栈和前端项目构建:网盘前端用到的是 React + Mobx,Web Server 用的是 Django,我们还简单介绍了 webpack、babel 和 gulp 等工具,点此回顾。
今天我们讲第三部分: 如何上传文件。
整个文件上传功能分为两部分:
获得用户的文件
将文件上传到服务器

获得用户文件的方法主要有两种:
通过 input 标签
通过拖拽(drag and drop)
HTML 中有一个 input 元素,默认情况下在页面上看到一个文本输入框,用户可以在里面输入文字。当把 input 的 type 改成 file 的时候,它会变一个按钮,当用户点击按钮时会弹出文件选择窗口,用户点击确定后会看到页面上显示刚才选择的文件名称。
当用户选择文件并且点击确定后会触发 input 的 onchange 事件,通过 e.target.files 可以得到一个包含用户选择文件的数组。如果我们把这个数组打印出来会看到文件的名称、大小和类型等。
Input 元素的其他属性简介:
multiple, 表示可以同时选择多个文件
accept, 表示可以选择的文件类型
webkitdirectory, 表示可以选择文件夹
获得用户文件的第二种方法是通过拖拽。首先,我们需要在页面上先定义一个接受 drop 事件的区域。比如现在我们页面上有一个 div,需要给这个 div 绑定 DragEnter 和 DragOver 事件,并且在这两个事件回调中 preventDefaut,这样这个区域才能接受 drop 事件。
Drag and drop 是通过 DataTransfer 这个对象来获取文件。
DataTransfer 这个对象中有一个由 DataTransferItem 组成的数组,在 DataTransferItem 上调用 getAsEntry 方法会得到 FileEntry 或者 FolderEntry。如果得到的是 FolderEntry,那还需要借助 FileReader 读取这个 FolderEntry 下所有的 FileEntry。
最后当我们得到了所有的 FileEntry 之后,就可以在 FileEntry 上调用 file 方法就会得到对应的 File 对象。
相比于前面通过 input 方式获得用户文件,通过拖拽方式获得用户文件有两个优点:
它有更好的用户体验,用户可以直接选中要拖拽的文件然后 drop 到页面上,不用在去找要上传的文件。
可以异步获取用户文件,在浏览器加载文件的过程中,不会阻塞用户的其他操作。
接下来我们看看如何将用户选择的文件上传到服务器。
文件上传到服务器

如上图所示,我们将前面所讲的获取用户文件的功能抽象成一个 input 模块,它会将 File 对象传给 uploader。uploader 模块用于存储上传状态和调度整个上传过程,它是整个文件上传功能的核心。
Proxy 模块负责将文件发送到服务器,它是唯一和服务器交互的模块,对 uploader 来说,proxy 隐藏了和服务器交互的具体细节,只返回文件的上传状态。图中还有一个 progress-view 模块,它是一个 view 负责从 uploader 中获取上传状态展示给用户。
一个文件的上传状态有:待上传,上传中,上传成功,上传失败和取消上传,他们直接的状态流转如上图所示。
为了实现这些状态直接的转化,我们分别为他们定义一个数组,数组中存储对应状态文件的 upload ID。当开始上传一个文件时,将对应的 upload ID 从待上传数组中取出,然后将其 push 到正在上传数组中,这样就实现了将文件的状态从待上传变成上传中,然后我们再把这个文件交给 proxy 模块让其去上传文件。
同理,当文件上传成功之后,再把 upload ID 从正在上传数组中取出,push 到上传成功数组中就实现了状态从正在上传到上传成功的转化。
为了实现能同时上传多个文件并把并行数量限制在一个合理的范围内,我们需要写一个 while 循环,循环的终止条件是正在上传的文件数量超过并行最大数量。在循环体中,不断重复上面说的步骤,当一个文件上传成功后,又会再次调用上面的 sendNextFile 方法,发送下一个文件。
Proxy 模块负责直接和服务器交互,包括上传文件之前的预请求,上传文件和返回上传结果给 uploader。一般的,每次的上传请求都需要签名,或者每次上传的 URL 都与文件名称有关,所以可以将这些前置操作都放在预请求中。预请求之后就可以开始上传文件了,如果文件的体积很小,可以一次请求就完成上传,如果文件的体积较大,则需要分段上传文件。
分段上传文件一般分为三个步骤,首先要初始化一个分段上传,向服务器请求一个分段上传 ID,然后开始上传分段,在上传分段的请求中需要带上前面得到的分段上传 ID,当所有的分段都上传完成之后,需要再发送一个分段上传完成请求,告知服务器所有的分段都上传完毕。
不管是上传小文件还是分段上传大文件,proxy 最后都会将文件上传的最后结果传给 uploader,uploader 再收到结果后会改变相应文件的状态,然后把下一个要上传的文件传给 proxy。

一个完整的文件上传功能可以分成两个部分,一是获得用户的文件,可以使用 input 标签或者拖拽 API,二是将文件上传到服务器,这部分又可以分成上传调度模块和发送上传请求的模块。
往期文章回顾:
知行学院总结 | Anybox Web 网盘技术栈、前端项目的构建过程与功能实现
知行学院总结 | 真正多活,不惧宕机——Region及同城多活方案分享
- FIN -
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

随时掌握互联网精彩
- 1 长江的美丽变奏 7985564
- 2 中央一号文件:推进农村高额彩礼治理 7920560
- 3 DeepSeek预测《哪吒2》最终票房 7825257
- 4 小包裹折射中国经济澎湃动能 7711158
- 5 不允许城镇居民到农村买农房、宅基地 7661466
- 6 国防部深感诧异强烈不满 7502831
- 7 马库斯被北京的空气质量震惊了 7448101
- 8 终于有部剧还原了我的高清童年 7391367
- 9 王曼昱4比0胜孙颖莎 首夺亚洲杯冠军 7218293
- 10 阮晓东高学历简历被质疑 7164525