用 md5 识别文件
根据 md5 来唯一识别一个文件。如果两个文件的 md5 相同,则认为它们是同一个文件。
区分 entity 表和 record 表
- entity 表:文件实体表,存储文件实体的信息:文件的 md5、文件的大小、文件的存储路径等。
- record 表:文件记录表,存储业务信息:文件的上传者、上传时间、对应的文件实体的 entityId等。
如果五个用户上传了同一个 PPT 文件。entity 表中只会有一条记录;record 表中会有5条记录,这5条记录的 entityId 是相同的。
秒传的原理
上传文件之前,浏览器先计算出文件的 md5 值并发送给服务器(只发送 md5 ,不发送文件实体)。
服务器收到 md5 值之后,在 entity 表中查询是否有相同的文件。如果查到了,那么这次“上传”就不需要真的去传输文件了,可以直接向 record 表中添加记录,也就实现了秒传。
分块上传的原理
上传文件之前,浏览器把文件切成一块一块的,然后分别发送给服务器。在发送每一小块时,除了传输当前块的内容,还需要携带如下信息:
- 文件的 md5 值
- 文件大小
- 文件切成多少块
- 当前块是第几块
服务器也是一块一块进行接收。有三个关键点:
- entity 表中有一条当前实体文件的数据。这条数据的上传状态 status 的值初始化为0,当所有块都收到后才把status的值改为1,标志着文件上传完成。
- .temp 文件:是“拼凑中”的文件,初始化为空。服务器收到每个块时,就把块的内容写进 .temp 文件相应的位置。
- .progress 文件:记录进度的文件。
- 初始化进度:根据文件总块数进行初始化。如果文件共分成2块,则初始化为"00"。如果文件共分成4块,则初始化为“0000”。
- 记录进度:服务器收到每个块时,就把 .progress 文件中块对应位置的0改为1。
断点续传的原理
服务器在收到每一块的时候,都需要拿着 md5 值去 entity 表中查询。
如果在 entity 表中查到了记录并且 status 为0,说明这个文件传过但是没有传完。服务器去对应的 .progress 文件中查询并把进度返回给浏览器。
这样,浏览器只会传输没传过的块了,也就实现了断点续传。
上传流程
-
上传前校验
校验剩余空间是否足够(传参fileSize),校验文件路径和文件名中是否包含敏感词(传参filePath)
校验成功,则开始计算md5;校验失败,则流程结束。 -
计算md5
使用分块、排队、多线程这三个策略缓解计算压力,具体看 计算文件 MD5。
计算 md5 完成之后,开始上传文件。 -
上传
先发送一个 GET 查询请求,询问该文件是否秒传或者断点续传- 如果是秒传,页面不发送任何一个块的 POST 请求,直接显示文件的上传进度100%。直接开始上传后校验。
- 如果是断点续传,服务器告诉浏览器还缺少哪些块。浏览器把剩下的块用 POST 请求一个一个发给后台。
- 如果都不是,则把文件的所有块用 POST 请求一个一个发给后台。
服务器收到所有的块之后,上传完成,开始上传后校验。
-
上传后校验
对文件内容进行敏感词校验。
如果校验成功,服务器向 record 表中新加一条数据,浏览器页面中显示文件上传成功。