web系列之打开文件夹showDirectoryPicker

前言

通过浏览器直接打开文件夹的功能,用户可以在不离开浏览器的情况下,轻松管理本地文件,实现浏览器打开文件夹及读取文件的操作。

本文主要介绍Web API中的showDirectoryPicker()方法,并实现一个类似vscode打开文件的功能。

1. showDirectoryPicker介绍

showOpenFilePicker() 方法用于显示一个文件选择器,以允许用户选择一个或多个文件并返回这些文件的句柄。通过这个对象,可以读取文件夹中的文件列表,可以对文件夹进行遍历和操作。

具体属性参考: MDN-showOpenFilePicker

2. 实现网页版vscode打开文件功能

案例: 实现一个按钮读取文件夹,并读取任意文件的内容进行展示。

具体实现过程:

  1. 通过showDirectoryPicker读取文件夹内容,通过返回的句柄生成目录结构
  2. 点击文件,通过句柄得到file对象,获取内容
  3. 使用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>

在上面的代码中:

  1. 确定访问权限:我们可以看到如何使用showDirectoryPicker()方法打开文件夹,此时会询问是否有查看的权限。
  1. 遍历文件结构:通过processHandle()函数对文件夹进行遍历,processHandle()函数是一个递归函数,它会遍历文件夹中的所有子项,并将它们添加到handle.children数组中。这样我们就可以在控制台中看到文件夹的完整结构。
  1. 读取文件内容:点击文件夹中的任意文件,并通过getFile()方法获取了它的File对象。使用FileReader对象读取了文件的内容,并在控制台中打印出来。
  1. 最后通过highlightjs进行代码装饰即可。

效果如下:

3. 总结

最后总结一下:

  • 通过Web API中的showDirectoryPicker()方法实现浏览器打开文件夹的功能,返回一个代表该文件夹的DirectoryHandle对象。通过这个对象,可以获取层级结构。点击任一文件通过getFile()方法获取了它的File对象。使用FileReader对象读取了文件的内容并展示。

如有错误,请指正O^O!

相关推荐
Smile_Gently1 小时前
前端:最简单封装nmp插件(组件)过程。
前端·javascript·vue.js·elementui·vue
luckycoke8 小时前
小程序立体轮播
前端·css·小程序
一 乐8 小时前
高校体育场管理系统系统|体育场管理系统小程序设计与实现(源码+数据库+文档)
前端·javascript·数据库·spring boot·高校体育馆系统
懒羊羊我小弟8 小时前
常用Webpack Loader汇总介绍
前端·webpack·node.js
祈澈菇凉8 小时前
ES6模块的异步加载是如何实现的?
前端·javascript·es6
我爱学习_zwj9 小时前
4.从零开始学会Vue--{{组件通信}}
前端·javascript·vue.js·笔记·前端框架
顾比魁9 小时前
XSS盲打:当攻击者“盲狙”管理员
前端·网络安全·xss
黑客老李9 小时前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
java·运维·服务器·前端·xss
晚风予星9 小时前
简记|LogicFlow自定义BPMN元素节点
前端
Json____9 小时前
使用html css js 开发一个 教育机构前端静态网站模板
前端·css·html·js·前端学习·企业站·教育机构网站