乐趣区

关于前端: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>
退出移动版