功能分析
文件上传功能是许多人都会用到的,以最基本的文件上传为例,我们可以在网页中添加一个input元素,并通过JavaScript监听该元素的change事件来获取文件。不管是通过formData还是直接将二进制数据放入请求体中,都能实现文件的上传
然而,随着需求的增加,原本简单的上传方法已不再适用。例如,当我们需要上传大型文件、实现断点续传、进行多文件上传或实现文件的秒传时,就必须对文件上传的整个流程进行重新设计
上传大文件
我们知道,一个大文件如果直接上传而不进行处理的话,会有着以下问题:
- 浏览器和网络的限制
如果上传过程中发生错误或网络中断,需要重新上传整个文件,无法实现断点续传,这会非常耗时,尤其是大文件 - 内存占用
将大文件加载到内存中会占用大量资源,尤其是在内存较小的设备(如移动设备)上,可能导致浏览器崩溃或应用程序卡顿 - 服务器处理压力大
服务器接收大文件时会占用较多资源,可能导致服务器性能下降,尤其是在并发上传时
文件分块
因此,对于大文件的处理,我们通常会将其分割成多个小文件来上传。每个小文件都通过一个独立的请求上传,这样如果有文件上传失败,我们只需重新上传那个特定的文件,而无需重新上传整个大文件。所有小文件上传完毕后,后端便可以执行文件的合并工作
文件合并
当前端上传文件切片时,我们需要将这些切片存储在某处。为了确保每个切片的唯一性,我们可以采用文件的hash作为切片的文件名
当所有切片上传完毕后,后端需要按照切片的顺序读取并组合这些切片,以创建完整的文件。同样,为了确保文件的唯一性,我们可以在文件名中加入文件的hash
页面阻塞
无论是对文件还是对切片进行hash计算,这一过程都是在客户端完成后上传至后端的。由于hash计算是CPU密集型任务,若全部在主线程中进行,将导致页面响应缓慢,文件越大,影响越严重。因此,我们需要利用多线程技术,即webWorker来解决这一问题
尽管采用了webWorker,客户端处理文件及其切片的hash仍然耗时依旧会很长。通常,我们会在计算出文件的hash后会向服务器查询该文件是否需要上传;如果服务器上不存在该文件,则继续计算切片的hash并上传。但客户端不能无限期地等待hash计算的完成,这要求我们必须优化现有流程
我们可以大胆做出假设,用户所选择的文件都是需要上传的新文件,所以我们可以跳过计算文件hash的步骤,直接上传切片和切片的hash,以提高效率
秒传
实际上,不管用户上传的是文件片段还是完整文件,都存在后端已保存该文件的可能性。在这种情况下,我们需要终止上传过程。我们可以在传输文件数据时验证文件的哈希值;如果服务器上已存在该文件,则立即终止本次请求,否则继续接收文件
接口设计
基于以上分析,我们可以设计出以下后端接口
emerge
- 接口类型:POST
- 接口名称:/emerge
- 参数位置:请求体
- 描述:请求将指定分片合并为一个指定文件
- 请求参数:
名称 类型 必选 说明 name string 是 文件名称 hashs [string] 是 文件切片hash数组 fileHash string 是 文件hash
exist
- 接口类型:GET
- 接口名称:/exist
- 参数位置:请求参数
- 描述:询问服务器当前文件或切片是否存在
- 请求参数:
名称 类型 必选 说明 hash string 是 当前hash isFile boolean 否 当前文件是文件还是切片 filename string 否 文件名
file
- 接口类型:POST
- 接口名称:/file
- 参数位置:请求体
- 描述:上传切片
- 请求参数:
名称 类型 必选 说明 hash string 是 切片hash file string(binary) 是 切片二进制数据
前端设计
基于以上分析,我们可以明确地将前端代码划分为三大类别
文件处理
在文件处理类别中,代码主要负责文件的切片处理、hash计算、多文件上传任务的执行顺序控制,以及提供任务执行所需的数据。为了实现这些功能,我们可以设计以下三个类
- Chunk
负责处理文件的分块,每个Chunk实例代表文件的一个分片 - BigFile
管理大文件上传的整体流程,确保数据的完整性和上传效率 - BigFileList
用于管理和控制多个大文件的上传过程,包括多文件的并发上传和任务调度
任务调度
任务类代码专注于文件的上传逻辑,包括断点续传、秒传等核心功能。在这一类别中,我们可以构建以下两个关键类
- Task
主要负责管理文件分块的上传任务,功能包括验证文件分块的存在性、执行上传操作、取消上传 - TaskQueue
负责管理和调度一组任务,实现任务的并发执行,整体进度监控和自动化管理
用户界面展示
展示类代码负责用户界面的展现,包括具体的按钮事件绑定等交互功能,我们可以设计以下两个类:
- FileDOM
用于创建与文件操作相关的DOM元素,并提供一系列方法来操作这些元素 - DOM
负责与网页上的DOM元素进行交互,其主要功能包括文件的选择和管理