如果没有看过分析篇的铁铁可以先去看看分析篇: 功能分析篇
基础代码
html部分代码很简单:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
<link rel="stylesheet" href="./font/iconfont.css">
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<div class="main">
<input type="file" name="file" id="file" multiple>
<label for="file" class="file-label" draggable="true">请将多个文件拖拽到此处进行扫描</label>
<div class="buttons">
<div class="left">
<button class="file-choose">选择文件</button>
<button class="folder-choose">选择文件夹</button>
</div>
<div class="right">
<button class="list-stop">暂停全部任务</button>
<button class="list-start">开始全部任务</button>
<button class="list-delete">删除全部任务</button>
</div>
</div>
<div class="file-list">
<div class="list-title list-column">
<div class="list-row">文件名</div>
<div class="list-row">上传进度</div>
<div class="list-row">状态</div>
<div class="list-row">操作</div>
</div>
<div class="list-body">
</div>
</div>
</div>
<script src="./untils/spark-md5.js"></script>
<script src="./js/main.js" type="module"></script>
</body>
</html>
css部分受限于篇幅故不展示
文件展示模板
const fileItem = document.createElement('div');
fileItem.classList.add('list-column');
fileItem.innerHTML = `
<div class="list-row list-name">${filename}</div>
<div class="list-row">
<div class="progress" style="--percent: ${percent};">
<div class="progress-bar">
<div class="progress-bar-inner"></div>
</div>
<div class="progress-number">
<span class="progress-percent">${percent}</span>
<span class="progress-speed">${speed}</span>
</div>
</div>
</div>
<div class="list-row">
<div class="row-status">${this.status}</div>
</div>
<div class="list-row">
<div class="row-operate">
<div class="row-stop iconfont icon-zanting"></div>
<div class="row-start iconfont icon-kaishi"></div>
<div class="row-delete iconfont icon-shanchu"></div>
</div>
</div>
`;
整体效果
整体效果如下

DOM
DOM类用于获取页面相关元素并提供对应的事件绑定
核心方法
它的核心方法如下:
- addFile
接受一个BigFile类型的参数file
将文件的DOM元素添加到文件列表中显示 - onFileChange
接受一个函数类型的参数cb
添加事件监听器,监听文件按钮的点击事件和拖放事件
将处理好的file数组传入cb中 - onFolderChange
接受一个函数类型的参数cb
添加事件监听器,监听文件夹按钮的变化事件
将处理好的file数组传入cb中
相关实现
class DOM {
constructor() {
this.files = []
this.fileButton = document.querySelector('.file-choose');
this.folderButton = document.querySelector('.folder-choose');
this.fileDragger = document.querySelector('.file-label');
this.fileList = document.querySelector('.list-body');
this.fileListStopButton = document.querySelector('.list-stop');
this.fileListStartButton = document.querySelector('.list-start');
this.fileListClearButton = document.querySelector('.list-delete');
}
addFile(file) {
this.files.push(file);
this.fileList.appendChild(file.fileDOM.getDOM());
}
removeFile(file) {
this.files = this.files.filter(f => f !== file);
}
onFileChange(cb) {
this.fileButton.addEventListener('click', async (e) => {
const files = []
try {
const handleFiles = await showOpenFilePicker({
multiple: true
})
for (const item of handleFiles) {
const file = await handleFile(item)
files.push(file)
}
} catch (err) {
console.log(err);
return;
}
cb(files);
});
this.fileDragger.addEventListener('dragover', (e) => {
e.preventDefault();
});
this.fileDragger.addEventListener('dragenter', (e) => {
e.preventDefault()
});
this.fileDragger.addEventListener('drop', async (e) => {
e.preventDefault()
const files = Array.from(e.dataTransfer.files)
cb(files)
});
}
onFolderChange(cb) {
this.folderButton.addEventListener('change', cb);
}
}
从句柄到文件对象
无论是通过showOpenFilePicker还是通过showDirectoryPicker来获取文件,本质上获取的是文件的句柄对象,句柄对象无法直接使用,我们可以通过getFile方法来得到句柄对象对应的file对象
async function handleFile(item) {
const file = await item.getFile();
return file;
}
如果此时的句柄对象指向的是文件夹的话,我们就需要通过entries方法得到此文件的所有子文件,通过kind属性来判断此时的句柄对象是否指向文件
async processHandle(handle) {
if (handle.kind === "file") {
return;
}
const entries = await handle.entries();
handle.children = [];
for await (const entry of entries) {
const subHandle = entry[1];
handle.children.push(subHandle);
}
}
当然,如果文件夹里的子文件数量十分的多的话我们不断调用processHandle方法会造成页面阻塞,我们可以通过利用浏览器空余时间处理,利用多线程技术,当用户需要时在处理当前层数的子文件
具体关于浏览器文件系统相关API的内容可以看我这篇文章
未动笔,未来可寄
FileDOM
BigFileDOM类主要控制每个文件在页面的相关展示,功能绑定等
核心方法
它的核心方法如下:
- onStopEvent
为暂停按钮添加点击事件监听器 - onStartEvent
为开始按钮添加点击事件监听器 - onDeleteEvent
为删除按钮添加点击事件监听器,同时会将该文件对应的DOM元素移除 - getDOM
返回创建的DOM元素 - updateStatus
更新文件的状态 - updatePercent
更新文件的进度百分比 - updateSpeed
更新文件的传输速度 - createDOM
创建与文件相关的DOM结构
相关实现
class FileDOM {
constructor(filename) {
this.filename = filename;
this.status = "读取中";
this.percent = "0%";
this.speed = "0KB/s";
this.dom = this.createDOM();
this.stopButton = this.dom.querySelector('.row-stop');
this.startButton = this.dom.querySelector('.row-start');
this.deleteButton = this.dom.querySelector('.row-delete');
this.statusElement = this.dom.querySelector('.row-status');
this.percentElement = this.dom.querySelector('.progress-percent');
this.speedElement = this.dom.querySelector(".progress-speed");
this.processElement = this.dom.querySelector('.progress');
}
onStopEvent(cb) {
this.stopButton.addEventListener('click', cb);
}
onStartEvent(cb) {
this.startButton.addEventListener('click', cb);
}
onDeleteEvent(cb) {
this.deleteButton.addEventListener('click', (e) => {
this.dom.remove();
cb && cb();
});
}
getDOM() {
return this.dom;
}
updateStatus(status) {
this.status = status;
this.statusElement.innerText = this.status;
}
updatePercent(percent) {
this.percent = percent;
this.percentElement.innerText = this.percent;
this.processElement.style = `--percent:${this.percent};`;
}
updateSpeed(speed) {
this.speed = speed;
this.speedElement.innerText = this.speed;
}
createDOM() {
const fileItem = document.createElement('div');
fileItem.classList.add('list-column');
fileItem.innerHTML = `
<div class="list-row list-name">${this.filename}</div>
<div class="list-row">
<div class="progress" style="--percent: ${this.percent};">
<div class="progress-bar">
<div class="progress-bar-inner"></div>
</div>
<div class="progress-number">
<span class="progress-percent">${this.percent}</span>
<span class="progress-speed">${this.speed}</span>
</div>
</div>
</div>
<div class="list-row">
<div class="row-status">${this.status}</div>
</div>
<div class="list-row">
<div class="row-operate">
<div class="row-stop iconfont icon-zanting"></div>
<div class="row-start iconfont icon-kaishi"></div>
<div class="row-delete iconfont icon-shanchu"></div>
</div>
</div>
`;
return fileItem;
}
}
1392

被折叠的 条评论
为什么被折叠?



