JavaScript实现访问本地文件夹
这个功能放在之前是不可能实现的,因为考虑到用户的隐私,但是最近有一个新的api可以做到这一点。下面来进行一个简单的功能实现。
如何选择文件夹
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>打开文件夹</button>
<script>
var btn = document.querySelector('button');
btn.onclick=function() {
showDirectoryPicker()
}
</script>
</body>
</html>
我们调用showDirectoryPicker这个函数就可以实现一个选择文件夹的功能。
showDirectoryPicker()
options
可选选项对象,包含以下属性:
id
通过指定 ID,浏览器能够记住不同 ID 所对应的目录。当使用相同的 ID 打开另一个目录选择器时,选择器会打开相同的目录。mode
字符串,默认为"read"
,可对目录进行只读访问。设为"readwrite"
可对目录进行读写访问。startIn
一个FileSystemHandle
对象或者代表某个众所周知的目录的字符串(如:"desktop"
、"documents"
、"downloads"
、"music"
、"pictures"
、"videos"
)。用于指定选择器的起始目录。
返回值
一个 Promise
对象,会兑现一个 FileSystemDirectoryHandle
(en-US) 对象。
异常
AbortError
当用户直接关闭了目录选择器或选择的目录是敏感目录时将会抛出 AbortError。
如何得到文件夹中的文件/子文件夹
首先对于上面所写的东西,我们进行try catch的优化
try {
// 获得文件夹的句柄
const handle = await showDirectoryPicker();
}
catch {
//用户拒绝查看文件
alert('访问失败')
}
之后我们来看一下这个headler打印出来是什么
句柄的简单解释
对于“句柄”,在下一直停留在一知半解的认识层面,近日在下学习Windows编程,决定趁此机会将句柄彻底搞清楚。查阅了一些网络上的资料,发现网络上的讲解大概可以分为两类:一种是以比喻、类比的方式说明,这种方法虽然形象易懂,但并没有从原理上、本质上加以揭示,让人仍然想问“为什么?”、“怎么实现?”。另一种是给出源代码,无可厚非,这当然是最本质的说明了,但这样一来,又显得不够直观,初学者理解起来有一定的难度。鉴于此,在下尽微末之能,结合自己的愚见,在两者之间折中,用图解的方式来将原理呈现出来,做到一目了然。
这里需要说明:
1.这里将句柄所能标识的所有东西(如窗口、文件、画笔等)统称为“对象”。
2.图中一个小横框表示一定大小的内存区域,并不代表一个字节,如标有0X00000AC6的横框表示4个字节。
3.图解的目的是为了直观易懂,所以不一定与源码完全对应,会有一定的简化。
让我们先看图,再解释。
其中,图1是程序运行到某时刻时的内存快照,图2是程序往后运行到另一时刻时的内存快照。红色部分标出了两次的变化。
简单解释:
Windows是一个以虚拟内存为基础的操作系统,很多时候,进程的代码和数据并不全部装入内存,进程的某一段装入内存后,还可能被换出到外存,当再次需要时,再装入内存。两次装入的地址绝大多数情况下是不一样的。也就是说,同一对象在内存中的地址会变化。(对于虚拟内存不是很了解的读者,可以参考有关操作系统方面的书籍)那么,程序怎么才能准确地访问到对象呢?为了解决这个问题,Windows引入了句柄。
系统为每个进程在内存中分配一定的区域,用来存放各个句柄,即一个个32位无符号整型值(32位操作系统中)。每个32位无符号整型值相当于一个指针,指向内存中的另一个区域(我们不妨称之为区域A)。而区域A中存放的正是对象在内存中的地址。当对象在内存中的位置发生变化时,区域A的值被更新,变为当前时刻对象在内存中的地址,而在这个过程中,区域A的位置以及对应句柄的值是不发生变化的。这种机制,用一种形象的说法可以表述为:有一个固定的地址(句柄),指向一个固定的位置(区域A),而区域A中的值可以动态地变化,它时刻记录着当前时刻对象在内存中的地址。这样,无论对象的位置在内存中如何变化,只要我们掌握了句柄的值,就可以找到区域A,进而找到该对象。而句柄的值在程序本次运行期间是绝对不变的,我们(即系统)当然可以掌握它。这就是以不变应万变,按图索骥,顺藤摸瓜。
**所以,我们可以这样理解Windows **句柄:
数值上,是一个32位无符号整型值(32位系统下);逻辑上,相当于指针的指针;形象理解上,是Windows中各个对象的一个唯一的、固定不变的ID;作用上,Windows使用句柄来标识诸如窗口、位图、画笔等对象,并通过句柄找到这些对象。
下面,关于句柄,再交代一些关键性细节:
1.所谓“唯一”、“不变”是指在程序的一次运行中。如果本次运行完,关闭程序,再次启动程序运行,那么这次运行中,同一对象的句柄的值和上次运行时比较,一般是不一样的。
其实这理解起来也很自然,所谓“一把归一把,这把是这把,那把是那把,两者不相干”(“把”是形象的说法,就像打牌一样,这里指程序的一次运行)。
2.句柄是对象生成时系统指定的,属性是只读的,程序员不能修改句柄。
3.不同的系统中,句柄的大小(字节数)是不同的,可以使用sizeof()来计算句柄的大小。
4.通过句柄,程序员只能调用系统提供的服务(即API调用),不能像使用指针那样,做其它的事。
再回归正题。
处理句柄函数
async function processHandler(handle) {
if (handle.kind==='file'){
return handle
}
handle.children=[]
const iter = await handle.entries();//获得文件夹中的所有内容
//iter:异步迭代器
for await (const info of iter){
var subHandle = await processHandler(info[1]);
handle.children.push(subHandle)
}
return handle
}
如何得到文件内容
const root = await processHandler(handle);
// 获得文件内容
const file = await root.children[1].getFile();
const reader = new FileReader();
reader.onload=e=>{
// 读取结果
console.log(e.target.result)
}
reader.readAsText(file,'utf-8')
这里用到的就是一个很简单的文件读了。
下面是完整的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>打开文件夹</button>
<script>
var btn = document.querySelector('button');
btn.onclick=async function() {
try {
// 获得文件夹的句柄
const handle = await showDirectoryPicker();
const root = await processHandler(handle);
// 获得文件内容
const file = await root.children[1].getFile();
const reader = new FileReader();
reader.onload=e=>{
// 读取结果
console.log(e.target.result)
}
reader.readAsText(file,'utf-8')
}
catch {
//用户拒绝查看文件
alert('访问失败')
}
}
async function processHandler(handle) {
if (handle.kind==='file'){
return handle
}
handle.children=[]
const iter = await handle.entries();//获得文件夹中的所有内容
//iter:异步迭代器
for await (const info of iter){
var subHandle = await processHandler(info[1]);
handle.children.push(subHandle)
}
return handle
}
</script>
</body>
</html>
来源:juejin.cn/post/7268011328940769315