网页访问文件夹(仿vscode文件浏览功能)
使用showDirectoryPicker
函数访问电脑的文件夹
函数返回值为一个文件句柄,第一个属性为文件类型,第二个为名字
要生成文件树结构,可以使用一个单独的函数进行处理
// 处理句柄
async function processHandle(handle) {
if (handle.kind === 'file') {
// 不是文件夹
return handle
}
handle.children = []
const iter = await handle.entries()
// iter 异步迭代器
for await (const info of iter) {
const subHandle = await processHandle(info[1]) // 递归处理
handle.children.push(subHandle)
}
return handle
}
生成树形结构
然后根据生成的树形结构句柄,渲染出目录来
// 渲染目录 ul 默认为文件夹 li 默认为文件
function renderCatalogue(handle, dom, subNode) {
// 清空目录
dom.innerHTML = ''
let ulWrapper = null
if (subNode === null || subNode === undefined) {
ulWrapper = document.createElement('div')
ulWrapper.className = 'ul-wrapper'
ulWrapper.innerText = handle.name
} else {
ulWrapper = subNode
}
const ulInner = document.createElement('div')
ulInner.className = 'ul-inner'
ulWrapper.appendChild(ulInner)
ulWrapper.addEventListener('click', (event) => {
event.stopPropagation(); // 阻止冒泡
renderContent(handle, contents.querySelector('.content'));
// 展开或者收起 让ul下的所有元素隐藏或者显示
ulInner.style.display = ulInner.style.display === 'none' ? 'block' : 'none'
})
if (!('children' in handle)) {
// 如果没有子文件
dom.appendChild(ulWrapper)
}
handle.children.forEach(item => {
// 如果是文件
if (item.kind === 'file') {
const listItem = document.createElement('div')
listItem.className = 'list-item'
listItem.innerText = item.name
listItem.addEventListener('click', (event) => {
event.stopPropagation() // 阻止冒泡, 防止点击 li 时触发 ul 的点击事件
renderContent(item, contents.querySelector('.content'))
})
ulInner.appendChild(listItem)
} else {
// 如果是文件夹
const subNode = document.createElement('div')
subNode.className = 'ul-wrapper'
subNode.innerText = item.name
renderCatalogue(item, dom, subNode)
ulInner.appendChild(subNode)
}
})
dom.appendChild(ulWrapper)
}
根据文件类型渲染出具体内容来
// 渲染内容
async function renderContent(handle, dom) {
if (handle.kind === 'file') {
// 渲染文件内容
const file = await handle.getFile()
const content = await file.text()
dom.innerText = content
} else {
// 显示文件夹名称
dom.innerText = handle.name
}
}
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网页访问文件夹</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: #f5f5f5;
font-family: Arial, sans-serif;
}
button {
font-size: 1rem;
font-weight: bold;
color: #fff;
background-color: #007bff;
padding: 5px 10px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0069d9;
}
.contents {
display: flex;
justify-content: space-between;
width: calc(100% - 20px);
height: calc(100vh - 60px);
margin: 10px auto;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.left {
width: 30%;
max-height: calc(100vh - 60px);
background-color: #fff;
border: 1px solid #ccc;
overflow: auto;
}
.ul-wrapper {
width: auto;
padding: 5px 10px;
background-color: #fff;
border-left: 1px solid #ccc;
cursor: pointer;
}
.list-item {
width: auto;
padding: 5px 10px;
transition: background-color 0.3s ease;
border-left: 1px solid #ccc;
list-style: none;
cursor: pointer;
}
.list-item:hover {
background-color: #f5f5f5;
}
.right {
width: 68%;
padding: 10px;
max-height: calc(100vh - 60px);
background-color: #fff;
border: 1px solid #ccc;
overflow: auto;
}
.card-container {
display: flex;
flex-wrap: wrap;
}
.card {
width: 100px;
height: 100px;
background-color: #ddd;
margin: 10px;
display: inline-block;
transition: all 0.3s;
overflow: hidden;
cursor: pointer;
}
.card:hover {
background-color: #ccc;
}
</style>
</head>
<body>
<button>访问文件夹</button>
<div class="contents">
<div class="left catalogue">
</div>
<div class="right content"></div>
</div>
<script>
const btn = document.querySelector('button')
const contents = document.querySelector('.contents')
btn.addEventListener('click', async () => {
try {
const handle = await showDirectoryPicker()
// 处理句柄
const root = await processHandle(handle)
// 渲染目录
renderCatalogue(root, contents.querySelector('.catalogue'))
} catch (e) {
// 用户拒绝查看文件夹内容
}
})
// 处理句柄
async function processHandle(handle) {
if (handle.kind === 'file') {
// 不是文件夹
return handle
}
handle.children = []
const iter = await handle.entries()
// iter 异步迭代器
for await (const info of iter) {
const subHandle = await processHandle(info[1]) // 递归处理
handle.children.push(subHandle)
}
return handle
}
// 渲染内容
async function renderContent(handle, dom) {
dom.innerHTML = ''
if (handle.kind === 'file') {
// 渲染文件内容
const file = await handle.getFile()
const content = await file.text()
dom.innerText = content
} else {
// 渲染文件列表
const ul = document.createElement('div')
ul.className = 'card-container'
handle.children.forEach(item => {
const li = document.createElement('div')
li.className = 'card'
li.innerText = item.name
li.addEventListener('click', (event) => {
event.stopPropagation() // 阻止冒泡, 防止点击 li 时触发 ul 的点击事件
renderContent(item, dom)
})
ul.appendChild(li)
})
dom.appendChild(ul)
}
}
// 渲染目录 ul 默认为文件夹 li 默认为文件
function renderCatalogue(handle, dom, subNode) {
// 清空目录
dom.innerHTML = ''
let ulWrapper = null
if (subNode === null || subNode === undefined) {
ulWrapper = document.createElement('div')
ulWrapper.className = 'ul-wrapper'
ulWrapper.innerText = handle.name
} else {
ulWrapper = subNode
}
const ulInner = document.createElement('div')
ulInner.className = 'ul-inner'
ulWrapper.appendChild(ulInner)
ulWrapper.addEventListener('click', (event) => {
event.stopPropagation(); // 阻止冒泡
renderContent(handle, contents.querySelector('.content'));
// 展开或者收起 让ul下的所有元素隐藏或者显示
ulInner.style.transition = 'all 0.3s'
ulInner.display = ulInner.display === 'none' ? 'block' : 'none'
// 展开收起动画
if (ulInner.display === 'none') {
ulInner.style.height = '0px'
ulInner.style.opacity = '0'
ulInner.style.visibility = 'hidden'
} else {
ulInner.style.height = 'auto'
const { height } = ulInner.getBoundingClientRect()
ulInner.style.height = '0px'
ulInner.offsetHeight
ulInner.style.height = height + 'px'
ulInner.style.opacity = '1'
ulInner.style.visibility = 'visible'
}
})
if (!('children' in handle)) {
// 如果没有子文件
dom.appendChild(ulWrapper)
}
handle.children.forEach(item => {
// 如果是文件
if (item.kind === 'file') {
const listItem = document.createElement('div')
listItem.className = 'list-item'
listItem.innerText = item.name
listItem.addEventListener('click', (event) => {
event.stopPropagation() // 阻止冒泡, 防止点击 li 时触发 ul 的点击事件
renderContent(item, contents.querySelector('.content'))
})
ulInner.appendChild(listItem)
} else {
// 如果是文件夹
const subNode = document.createElement('div')
subNode.className = 'ul-wrapper'
subNode.innerText = item.name
renderCatalogue(item, dom, subNode)
ulInner.appendChild(subNode)
}
})
dom.appendChild(ulWrapper)
}
</script>
</body>
</html>