怎样写一个File DnD?

347 阅读5分钟

需求背景

用户可以任意拖拽文件/文件夹到页面上,不限制文件个数,自动分析里面的图片然后自动生成sprite图

7d65b1df4b124e8cb9e91f7922743f62.png (650×331) (csdnimg.cn)

  • 关于sprite图,现在常规的前端可能已经不在使用了
  • 如果是从事图形工作的同学应该知道其作为webgl纹理使用可以大大提高程序性能
  • 整个需求的要求要做到如下面的视频链接里所示
    sprite-creator-video

核心功能分析

  • file dnd HTML 拖放(Drag and Drop),对用户拖进去的文件或者文件夹进行自动分析
  • sprite 图的布局和生成(该功能这篇文章里不做介绍)

调研

  • github 找了半天没有找到好的 file dnd库,于是决定自己写一个file dnd
  • 如果小伙伴有好的相关的库推荐,可以在评论区@我

自己写一个File DND

查阅MDN

于是便去MDN Web Docs (mozilla.org)寻宝,看了相关API文档

看到这里一下子就豁然开朗了,整个前端已经可以判断文件和文件夹了,且文件的读取和文件夹的读取都支持,只需要对文件夹进行递归遍历读取就可以把所有的文件都读取出来了

设计和编码

  • 读取文件夹里FileSystemFileEntry集合
//from [FileSystemDirectoryEntry.createReader() - Web APIs | MDN (mozilla.org)](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryEntry/createReader)
function readDirectory(directory) {
  let dirReader = directory.createReader();
  let entries = [];

  let getEntries = () => {
    dirReader.readEntries((results) => {
      if (results.length) {
        entries = entries.concat(toArray(results));
        getEntries();
      }
    }, (error) => {
      /* handle error — error is a FileError object */
    });
  };

  getEntries();
  return entries;
}

注意FileSystemDirectoryReader 每次读取的文件数量是有限的,当一个文件夹文件特别多时FileSystemDirectoryReader只能读取前多少条的,所有需要一直循环读取,类似分页,一直分页下去,直至读取不到数据结束读取

  • 所以只要一直递归读取文件夹即可,把所有的文件读取出来存放到一个结果集合(FileSystemFileEntry Array)
  • 直到所有的FileSystemDirectoryEntry 读取结束,然后将所有的FileSystemFileEntry集合读取为File,简易的代码如下:
    let fileEntryList = [], isReading = false, idx = 0;
    const readFiles = () => {
        idx = 0;
        const readFile = () => {
            if (idx < files.length) {
                const fileEntry = files[idx];
                if (fileEntry.isDirectory) {
                    idx++;
                    readFile();
                } else {
                    fileEntry.file((file) => {
                        file.path = fileEntry.path || '/';
                        file.parentName = fileEntry.parentName;
                        file.id = fileEntry.id;
                        file.pid = fileEntry.pid;
                        file.isDirectory = !!file.isDirectory;
                        files[idx] = file;
                        idx++;
                        readFile();
                    });
                }
            } else {
                callback(files);
            }
        };
        readFile();
    };

树形数据结构的支持

55.png

  • 方便前端来展示用户拖进去的文件的目录结构
  • 在读取文件和文件夹时为每个节点负值id和pid,方便用来组装成树形结构
  • 并自动拼接每个文件的path地址,方便用户进行文件上传时后端自动根据path来自动创建对应的目录结构
  • 将平行的数组组装成树形结构即可
  • 简易代码如下
 toTree() {
        const files = this.files;
        const fileMap = {};
        files.forEach(file => {
            const { id, path, name, parentName } = file;
            if (!fileMap[id]) {
                fileMap[id] = {
                    id,
                    name,
                    label: name,
                    path,
                    parentName: parentName,
                    children: [],
                    isDirectory: file.isDirectory
                };
            }
        });
        files.forEach(file => {
            const { pid, id } = file;
            if (fileMap[pid]) {
                fileMap[pid].children.push(fileMap[id]);
            }
        });
        for (const id in fileMap) {
            const children = fileMap[id].children;
            const dirs = [], files = [];
            //模拟windows文件管理效果,把文件夹放到前面
            children.forEach(child => {
                if (child.isDirectory) {
                    dirs.push(child);
                } else {
                    files.push(child);
                }
            });
            fileMap[id].children = dirs.concat(files);
        }
        return Object.values(fileMap).filter(d => {
            return !d.parentName;
        });

    }

性能表现

  • 拖拽一个node_modules文件夹进去,里面包含30000个文件,解析无压力

提供独立的NPM包filednd

npm i filednd
#or
yarn add filednd

思维拓展

  • 既然已经可以拿到用户上传的文件集合了(File Array),也已经拿到整个文件的目录结构了,能否做一个类似vscode这样一个简单的编辑器?
  • 方法:
    • 将用户拖进去的文件目录的结构用树形展示
    • 当用户点击一个文件节点时,读取文件内容然后利用一些编辑器来展示
  • 演示视频
  • 当然这里只是做个简单的效果,主要强调的有了这个file dnd后是可以做出很多有意思的事情了
  • 如果有这方面的需求的同学可以加大在这方面的研究

总结

PS