前言
通过浏览器直接打开文件夹的功能,用户可以在不离开浏览器的情况下,轻松管理本地文件,实现浏览器打开文件夹及读取文件的操作。
本文主要介绍Web API中的showDirectoryPicker()
方法,并实现一个类似vscode打开文件的功能。
1. showDirectoryPicker介绍
showOpenFilePicker()
方法用于显示一个文件选择器,以允许用户选择一个或多个文件并返回这些文件的句柄。通过这个对象,可以读取文件夹中的文件列表,可以对文件夹进行遍历和操作。
具体属性参考: MDN-showOpenFilePicker
2. 实现网页版vscode打开文件功能
案例: 实现一个按钮读取文件夹,并读取任意文件的内容进行展示。
具体实现过程:
- 通过showDirectoryPicker读取文件夹内容,通过返回的句柄生成目录结构
- 点击文件,通过句柄得到file对象,获取内容
- 使用highlightjs进行代码修饰
具体代码如下:
html
<!DOCTYPE html>
<style>
.container {
display: flex;
height: 100vh;
}
.left {
width: 30%;
height: 100%;
overflow: auto;
padding: 16px;
border: 1px solid #999;
}
.right {
width: 70%;
height: 100%;
overflow: auto;
padding: 16px;
border: 1px solid #999;
border-left: 0;
}
.tree-node {
cursor: pointer;
/* 为伪元素设置定位上下文 */
position: relative;
}
.tree-node::before {
/* 使用Unicode字符表示箭头 向下箭头*/
content: '▾';
/* content: '▶'; */
display: inline-block;
/* 为箭头和文字之间添加间距 */
margin-right: 5px;
/* 平滑变换箭头方向 */
transition: transform 0.2s ease-in-out;
}
.tree-node.expanded::before {
/* 旋转箭头以表示展开状态 */
transform: rotate(-90deg);
}
.ml-20 {
margin-left: 20px;
}
.content {
height: 200px;
}
</style>
<body>
<div class="container">
<div class="left">
<button>打开文件夹</button>
<div id="folder"></div>
</div>
<div class="right">
<pre>
<code id="content">
</code>
</pre>
</div>
</div>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<!-- and it's easy to individually load additional languages -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/go.min.js"></script>
<script>
const folderNode = document.getElementById('folder')
const contentNode = document.getElementById('content')
const btnNode = document.querySelector('button')
btnNode.onclick = async () => {
//使用showDirectoryPicker()方法打开文件夹选择器,并获取文件夹句柄(handle)
const handle = await showDirectoryPicker()
await processHandle(handle)
// 将文件夹内容添加到页面中
let container = processHtml(handle)
folderNode.appendChild(container)
}
/**
* 读取文件内容
* @param {*} handle
* @returns
*/
async function readFile(handle) {
return new Promise(async (resolve, reject) => {
const file = await handle.getFile()
const fileReader = new FileReader()
fileReader.readAsText(file)
fileReader.onload = e => {
resolve(e.target.result)
}
})
}
/**
* 递归处理句柄
* @param {*} handle
* @returns
*/
function processHtml(handle) {
const isFolder = handle.kind === 'directory'
const div = document.createElement('div')
div.textContent = handle.name
div.classList.add('ml-20')
if (isFolder) {
div.classList.add('tree-node')
div.dataset.isFolder = true
// 添加折叠效果
div.addEventListener('click', function (e) {
// 如果点击的是文件夹,则进行展开或折叠
e.stopPropagation(); // 阻止事件冒泡,防止父级文件夹也被点击
// 切换展开/折叠状态
div.classList.toggle('expanded');
// 将子元素隐藏
// 获取所有的直接子元素
const childElements = div.children;
// 遍历所有的直接子元素并将它们隐藏
for (let i = 0; i < childElements.length; i++) {
let isHidden = childElements[i].style.display === 'none';
childElements[i].style.display = isHidden ? 'block' : 'none';
}
});
// 递归处理文件夹目录
if (handle.children.length) {
for (let i = 0; i < handle.children.length; i++) {
const element = handle.children[i];
const eleHtml = processHtml(element)
div.appendChild(eleHtml)
}
}
} else {
div.dataset.isFolder = false
// 添加背景色
div.style.backgroundColor = '#f0f0f0'
div.addEventListener('click', async function (e) {
e.stopImmediatePropagation(); // 阻止事件冒泡,防止父级文件夹也被点击
const content = await readFile(handle)
contentNode.innerText = content
hljs.highlightAll();
})
}
return div
}
/**
* 递归处理文件夹目录
* @param {*} handle
* @returns
*/
async function processHandle(handle) {
if (handle.kind === 'file') {
return;
}
handle.children = []
// 获取文件夹中的所有内容
const iter = await handle.entries()
// 返回异步迭代器
for await (const entry of iter) {
await processHandle(entry[1])
handle.children.push(entry[1])
}
}
</script>
</body>
</html>
在上面的代码中:
- 确定访问权限:我们可以看到如何使用
showDirectoryPicker()
方法打开文件夹,此时会询问是否有查看的权限。
- 遍历文件结构:通过
processHandle()
函数对文件夹进行遍历,processHandle()
函数是一个递归函数,它会遍历文件夹中的所有子项,并将它们添加到handle.children
数组中。这样我们就可以在控制台中看到文件夹的完整结构。
- 读取文件内容:点击文件夹中的任意文件,并通过
getFile()
方法获取了它的File
对象。使用FileReader
对象读取了文件的内容,并在控制台中打印出来。
- 最后通过highlightjs进行代码装饰即可。
效果如下:
3. 总结
最后总结一下:
- 通过Web API中的
showDirectoryPicker()
方法实现浏览器打开文件夹的功能,返回一个代表该文件夹的DirectoryHandle
对象。通过这个对象,可以获取层级结构。点击任一文件通过getFile()
方法获取了它的File
对象。使用FileReader
对象读取了文件的内容并展示。
如有错误,请指正O^O!