跳转到内容

网站访问用户文件夹

刀刀
0字
0分钟
2026/8/16

网页端可通过 File System Access API 访问用户本地文件夹。

JS 读取文件夹内全部文件

JS 读取文件夹选择文件夹 API

通过 showDirectoryPicker API 打开选择文件夹对话框,选择后需用户确认权限方可生效。代码如下:

js
btn.onclick = function () {
  console.log('click me')
  showDirectoryPicker()
}

JS 读取文件夹得到文件与子文件夹

showDirectoryPicker API 的返回值就是得到的文件或文件夹的句柄,返回如下:

js
{
  kind: 'directory',
  name:'xxx'
}

其中,kind 表示得到的类型,directory 表示文件夹,file 表示文件。name 为该文件夹或文件的名称。

获取可能失败,用户可能拒绝访问,用 try...catch 捕获错误。

拿到了文件夹时,可通过 .entries() 方法获取文件夹内所有内容,返回值是异步迭代器,通过 for await of 循环拿到子文件句柄。句柄返回内容如下:

js
;([
  'folder-request',
  {
    kind: 'directory',
    name: 'folder-request',
  },
],
  [
    'demo.html',
    {
      kind: 'file',
      name: 'demo.html',
    },
  ])

由此可拿到文件夹或文件,通过递归遍历获取句柄,直到全部为文件。代码如下:

查看代码
js
btn.onclick = async function () {
  try {
    const handle = await showDirectoryPicker()
    const root = await processHandle(handle)
  } catch (err) {
    // ...
  }
}

async function processHandle(handle) {
  // 添加判断,终止递归,返回文件
  if (handle.kind === 'file') {
    return handle
  }

  handle.children = []
  const iter = await handle.entries() // 获取文件夹中所有内容
  for await (const info of iter) {
    const subHandle = await processHandle(info[1]) // 返回的是一个数组,返回的内容格式如上所述。通过递归的思想一直获取文件夹内的内容
    handle.children.push(subHandle)
  }

  return handle
}

现在 root 能够拿到一个树状结构的句柄。

JS 读取文件夹读取文件

通过 getFile() 可以获取文件内容,使用 FileReader() 类中的 readAsText 方法就能读取文件内容,该读取操作是异步操作,通过 onload 函数能够最终获取结果。代码如下:

js
btn.onclick = async function () {
  try {
    const handle = await showDirectoryPicker()
    const root = await processHandle(handle)

    // 获取内容
    const file = await root.children[1].getFile()
    const reader = new FileReader()
    reader.onload = (e) => {
      console.log(e.target.result)
    }
  } catch (err) {
    // ...
  }
}

// ...

JS 文件夹其他操作

JS 文件夹多选

要从多个文件中读取,传入 options 对象给 showOpenFilePicker(),默认 multiplefalse,多选需设为 true

js
let fileHandles
const options = { multiple: true }
const pickFile = async () => {
  fileHandles = await window.showOpenFilePicker(options)
}

其他选项可用于指示可以选择的文件类型。例如,只想接收 .png 文件,选项对象将包括以下内容

js
const options = {
  types: [
    {
      description: 'Images',
      accept: { 'image/png': '.png' },
    },
  ],
  excludeAcceptAllOption: true,
}

多选后返回的结果是一个包含多个文件的数组,因此获取它们的内容将通过以下方式完成:

js
const pickFile = async () => {
  const fileList = await window.showOpenFilePicker({ multiple: true })
  const allContent = await Promise.all(
    fileList.map(async (fileItem) => {
      const file = await fileItem.getFile()
      const content = await file.text()
      return content
    }),
  )
  console.log(allContent)
}

JS 文件夹写文件

写文件的操作分为以下几步:

  1. 通过 createWritable() 返回一个 FileSystemWritableFileStream 对象
  2. 调用 write() 将需要传递内容写入此流
  3. 调用 close() 关闭流
js
btn.onclick = async function () {
  try {
    const handle = await showDirectoryPicker({
      types: [
        {
          description: 'Test files',
          accept: {
            'text/plain': ['.txt'],
          },
        },
      ],
    })

    // 写文件
    const writable = await handle.createWritable()
    await writable.write('hello world')
    await writable.close()
  } catch (err) {
    // ...
  }
}

这样就能写一个 html 内容。

JS 文件夹编辑文件内容

编辑文件的本质是先通过 showOpenFilePicker()getFile() 方法读取文件,然后使用createWritable() , write()close() 写入同一个文件内,实现编辑操作。

js
let fileHandle
const writeFile = async () => {
  ;[fileHandle] = await window.showOpenFilePicker()
  const file = await fileHandle.getFile()
  const writable = await fileHandle.createWritable()
  await writable.write('这是新写入的内容')
  await writable.close()
}

JS 文件夹删除文件

调用 remove() 方法可以删除全部文件或文件夹;传入对应文件名称可以指定删除的文件或文件夹。

js
const removeDirectory = async () => {
  const directoryHandle = await window.showDirectoryPicker()
  await directoryHandle.remove()
}

// 删除选定文件夹中名为data.txt的单个文件
const removeSelfFile = async () => {
  const directoryHandle = await window.showDirectoryPicker()
  await directoryHandle.removeEntry('data.txt')
}

// 递归删除名为“data”的文件夹
const removeAllDirectory = async () => {
  const directoryHandle = await window.showDirectoryPicker()
  await directoryHandle.removeEntry('data', { recursive: true })
}

JS 浏览器兼容性

can i use版本

JS 总体效果

跳转预览:点击跳转

贡献者

The avatar of contributor named as duyidao duyidao
The avatar of contributor named as 平安喜乐 平安喜乐
The avatar of contributor named as v_duyilin v_duyilin
The avatar of contributor named as 刀刀 刀刀
The avatar of contributor named as Orange Orange

页面历史

刀刀博客累计访客 人;文档累计访问量共