写在前面
前段时间在工作中接触到了文件上传的内容,但业务中实现的功能比较简单,于是我想着能不能使用纯原生的方式实现一个大文件的上传DEMO,从而在本质上学习大文件上传的思路。本教程使用纯原生的html+node.js实现,能快速上手一个简单的大文件上传,深入理解其内部的原理,也能方便在后续的工作中对DEMO进行快速扩展,非常适合想入门学习大文件上传的同学。点击链接可以查看原文地址,我的博客会不定期更新一些简单易懂的前端教程,关注Github可以不走丢。
效果展示
首先来看看最后的效果。
下面👇是在线DEMO和源码等资源地址。
实现思路
上图是大文件上传的整体流程图,显示了客户端和服务端的交互逻辑,方便大家从宏观上理解大文件上传的过程,但如果按照上面的流程讲解大文件上传入门,很容易被劝退。
下面👇我们将按照功能点逐步迭代的方式讲解大文件上传,每个功能点都很简单,每实现一个功能点都会极大的增涨我们的信心。大文件上传一共分为分片上传、分片合并、文件秒传、断点续传、上传进度这五个功能点,后面的功能都是在前面的功能基础上迭代完成。如果能实现一个分片上传功能就算是入门了大文件上传了,后面都是在此基础上增加功能而已。
具体实现
分片上传
首先我们来实现一个最简单也最核心的分片上传,这个功能点分为客户端的文件分片、计算hash值、上传分片文件和服务端的创建分片目录并存储分片。客户端和服务端源代码分别存放在BigFileUpload.html
和server.js
文件中。
客户端
为了方便后面能够处理取消上传和上传进度,我们首先对fetch
请求做一个简单的封装。
下面👇是分片功能需要的标签元素。
首先,我们需要使用slice()
方法对大文件进行分片,并把分片的内容、大小等信息都放入到分片列表中,最后在页面上显示一下分片数量。
然后, 使用spark-md5 分别计算每个分片的hash值,最后得到整个文件hash值。计算hash值需要比较长的时间,可以在页面上输出计算hash值的进度。
紧接着,需要将分片数据全部上传到服务器,这里需要注意是的分片的hash值是 ${fileHash}-${index}
, 服务端会根据这个hash值创建分片文件。
服务端
首先,我们使用原生node.js
启动一个后端服务。
接下来,我们就可以在里面添加上传分片的接口。使用multiparty读取到客户端提交的表单数据后,判断切片目录是否存在,不存在就使用 fileHash
值创建一个临时的分片目录,并使用fs-extra 的move
方法存储文件分片到对应的分片目录下。
到这里为止,我们就已经实现了文件上传最基本的功能,后续只是在此基础上进行迭代。
合并分片
客户端
在上传完文件分片之后,我们就可以对所有文件分片进行合并,这里需要请求一个合并分片的接口,需要传递文件的fileHash
和 filename
。
服务端
合并切片功能最核心的功能就是根据fileHash
读取对应分片目录下的分片文件列表,并按照分片下标进行排序,避免后面合并时顺序错乱。然后,使用 writeFile
方法创建一个空文件,再使用appendFileSync
依次向文件中添加分片数据,最后删除临时的分片目录。
这里实现一下合并分片的接口,首先需要读取请求中的数据,然后拼接出合并后的文件名称 ${UPLOAD_DIR}/${fileHash}${ext}
,最后调用合并分片方法。
秒传
客户端
实现秒传只需要在文件上传之前请求接口验证一下文件是否存在。
服务端
如果文件存在shouldUpload
就返回 false
,否则就返回 true
。
断点续传
客户端
断点续传新增了两个按钮,来控制文件上传进度。
这里需要对requestApi
进行一些改造,添加 abortControllerList
用于存储需要被取消的请求,如果接口请求成功,则将fetch
从 abortControllerList
中移除。
在分片上传也需要做一些改造,将接口中获取到的uploadedList
,从所有分片列表中过滤出去,当已上传的uploadedList
数量加 requestList
的数量等于分片列表fileChunkListData
的数量时才进行分片合并。
然后,实现一下暂停和恢复的事件处理,暂停是通过调用 AbortController 的 abort()
方法实现。恢复则是重新获取uploadedList
后再进行分片上传实现。
服务端
断点续传是在秒传接口的基础上实现的,只是需要新增已上传分片列表uploadedList
。
上传进度
上传进度只需要改造客户端,首先,新增显示进度的标签。
上传进度需要对fetch
请求再做一点改造,这里需要使用getReader()
手动读取数据流,获取到当前上传进度,并添加onProgress
回调。
然后,在上传的时候将已上传进度设置成100,并添加onProgress
回调处理,累计每个分片的进度,得到整体的上传进度。
总结
大文件上传其实很多时候不需要我们自己去实现,因为已经有很多成熟的解决方案。
但深入理解大文件上传背后的原理,更加有利于我们对已有的大文件上传方案进行个性化改造。
在线实现大文件上传的过程中使用到了三个插件,multiparty、fs-extra、spark-md5,如果大家不太理解,需要自己去补充一下相关知识。
扩展阅读
打包发布