# 上一篇文章
【文件上传系列】No.2 秒传(原生前端 + Node 后端)
断点续传效果展示
准备工作:暂停上传
前端点击暂停按钮,之后把所有上传的请求取消掉,需要使用 xhr.abort() 这个方法,点我跳转到 MDN 。
写个按钮,监听点击事件
1 2 3 4 5 6 7 8 9 10 11 12 13
| <body> <button id="pauseButton">暂停</button> </body>
<script>
document.getElementById('pauseButton').addEventListener('click', () => { console.log(xhrList); xhrList.forEach((xhr) => xhr.abort()); }); </script>
|
这里的 xhrList
是我们在上传的时候传递给 request
的
来测试一下暂停后是否能拿到有效的 xhrList
:
ok,暂停可以,顺便把页面的按钮显示和隐藏写一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| (function () { hiddenButton('resumeButton'); hiddenButton('pauseButton'); })();
function hiddenButton(id) { document.getElementById(id).setAttribute('style', 'display: none'); } function displayButotn(id) { document.getElementById(id).setAttribute('style', 'display: inline-block'); }
function handleButton(option) { hiddenButton('uploadButton'); displayButotn('pauseButton');
document.getElementById('pauseButton').addEventListener('click', () => { xhrList.forEach((xhr) => xhr.abort()); hiddenButton('pauseButton'); displayButotn('resumeButton'); });
document.getElementById('resumeButton').addEventListener('click', async () => { handleVerify(option); hiddenButton('resumeButton'); displayButotn('pauseButton'); }); }
|
接下来开始断点续传!
思路:断点续传
- 我们在上传之前都要去验证一下,服务器中
是否存在已经上传的文件块
,然后后端返回已经上传的文件块数组 index,前端处理的时候过滤掉已经上传的块即可。
实现断点续传
前端:过滤已上传的 chunk 以及进度条改进
断点续传需要验证的地方大概有这几个
因为验证的函数是通用的,所以把验证的逻辑封装成函数,大概的逻辑如下图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
async function handleVerify(option) { const { fileHash, fileName, fileChunkList } = option; const { code, message, hadUploadedChunksList } = await handleVerifyHash(fileHash, fileName); const hadUploadedChunksListMap = {}; hadUploadedChunksList.forEach((item) => (hadUploadedChunksListMap[item.split('_')[1]] = true)); if (code === 0 || code === 1) { const hanldleData = fileChunkList.map(({ file }, index) => { return { chunk: file, index, }; }); await uploadChunks(hanldleData, fileName, fileHash, hadUploadedChunksListMap); } if (code === 2) { alert('文件已秒传'); } }
|
然后在上传的函数里把已上传的过滤掉
后端:验证接口
整理代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
async handleVerify(req, res, MERGE_DIR, UPLOAD_DIR) { try { const postData = await handlePostData(req); const { fileHash, fileName } = postData; const ext = path.extname(fileName); const willCheckMergedName = `${fileHash}${ext}`; const willCheckPath = path.resolve(MERGE_DIR, willCheckMergedName);
const chunkPathOfFile = path.resolve(UPLOAD_DIR, fileHash); const hadUploadedChunksList = (await fse.existsSync(chunkPathOfFile)) ? await fse.readdir(chunkPathOfFile) : []; console.log('hadUploadedChunksList:>>', hadUploadedChunksList); if (fse.existsSync(willCheckPath)) { res.end( JSON.stringify({ code: 2, message: 'existed', hadUploadedChunksList, }) ); } else if (fse.existsSync(chunkPathOfFile)) { res.end( JSON.stringify({ code: 1, message: 'not all existed', hadUploadedChunksList, }) ); } else { res.end( JSON.stringify({ code: 0, message: 'no exist', hadUploadedChunksList, }) ); } } catch (err) { console.log(err); } }
|
这样一个断点续传的功能就做好啦~
参考文章
- 字节跳动面试官:请你实现一个大文件上传和断点续传