Skip to content

文件夹上传调研

代码例子

vue
<template>
  <div
    @drop.prevent="dropHandler"
    @dragover.prevent
    style="height: 100vh; background-color: skyblue"
  >
    拖拽文件夹到这里
    <input
      type="file"
      webkitdirectory
      directory
      multiple
      @change="handleFiles"
    />
  </div>
</template>

<script setup>
import { ref } from "vue";

const MAX_DEPTH = 6;
const MAX_FILES = 3000;

async function traverseDirectory(entry, path = "", depth = 0) {
  if (depth > MAX_DEPTH) {
    throw new Error("文件夹超过6层,无法上传");
  }
  let files = [];
  const dirReader = entry.createReader();
  const entries = await new Promise((resolve) =>
    dirReader.readEntries(resolve)
  );

  for (const ent of entries) {
    if (ent.isFile) {
      files.push(ent);
    } else if (ent.isDirectory) {
      const subFiles = await traverseDirectory(
        ent,
        `${path}/${ent.name}`,
        depth + 1
      );
      files = files.concat(subFiles);
    }
  }
  return files;
}

const handleFiles = async (event) => {
  try {
    const items =
      event.type === "drop" ? event.dataTransfer.items : event.target.files;
    let files = [];

    for (let i = 0; i < items.length; i++) {
      const item = items[i].webkitGetAsEntry
        ? items[i].webkitGetAsEntry()
        : items[i];
      if (item) {
        if (item.isFile) {
          files.push(item);
        } else if (item.isDirectory) {
          const subFiles = await traverseDirectory(item);
          files = files.concat(subFiles);
        }
      }
    }

    if (files.length > MAX_FILES) {
      throw new Error("文件数超过3000,无法上传");
    }

    console.log("items", items);

    // 在这里添加上传任务
  } catch (error) {
    console.error("error", error);
  }
};

const dropHandler = (event) => {
  handleFiles(event);
};
</script>

上面代码存在的问题,现在拖放文件夹最多只能显示 100 个文件

你的代码看起来是用来处理拖放文件夹并读取文件夹中的文件的。这段代码对文件数量有一个上限,即 MAX_FILES,设置为 3000,如果文件数量超过这个数值,会抛出一个错误。对于文件夹的深度(即子文件夹的层级)也有一个限制,即 MAX_DEPTH,设置为 6,如果文件夹层级超过这个深度,也会抛出一个错误。

关于你提到的问题,只能显示 100 个文件,这可能是由于一些浏览器在处理 readEntries 方法时有一些限制。有些浏览器在执行 readEntries 方法时,一次只能读取到一定数量的文件或文件夹,例如 Chrome 浏览器就存在这样的限制,每次只能读取到 100 个文件或文件夹。

为了解决这个问题,你需要在一个循环中多次调用 readEntries 方法,直到所有的文件或文件夹都被读取。以下是一个修改后的 traverseDirectory 函数:

javascript
async function traverseDirectory(entry, path = "", depth = 0) {
  if (depth > MAX_DEPTH) {
    throw new Error("文件夹超过6层,无法上传");
  }
  let files = [];
  const dirReader = entry.createReader();
  let entries;

  do {
    entries = await new Promise((resolve) => dirReader.readEntries(resolve));
    for (const ent of entries) {
      if (ent.isFile) {
        files.push(ent);
      } else if (ent.isDirectory) {
        const subFiles = await traverseDirectory(
          ent,
          `${path}/${ent.name}`,
          depth + 1
        );
        files = files.concat(subFiles);
      }
    }
  } while (entries.length > 0);

  return files;
}

这段修改过的代码中,readEntries 方法被放在了一个 do-while 循环中,每次读取到的文件或文件夹会被处理,直到没有更多的文件或文件夹可以读取为止。这样就能读取到文件夹中的所有文件,而不仅仅是前 100 个。


现在获取到的 files 还不是 file 对象

js
async function traverseDirectory(entry, path = '', depth = 0) {
  if (depth > MAX_DEPTH) {
    throw new Error('文件夹超过6层,无法上传');
  }
  let files = [];
  const dirReader = entry.createReader();
  let entries;

  do {
    entries = await new Promise((resolve) => dirReader.readEntries(resolve));
    for (const ent of entries) {
      if (ent.isFile) {
        const file = await new Promise((resolve) => ent.file(resolve));
        files.push(file);
      } else if (ent.isDirectory) {
        const subFiles = await traverseDirectory(ent, `${path}/${ent.name}`, depth + 1);
        files = files.concat(subFiles);
      }
    }
  } while (entries.length > 0);

  return files;
}

不能同时选择文件和文件夹问题

上传文件和文件夹按钮分开 -

参考例子

  • 百度网盘 就不可以同时选择文件和文件夹
  • electron 官网也提示 Windows 和 Linux 不支持同时选择文件和文件夹

异步读取文件夹内容 无法读取到第二个

Promise.all的方式读取