【问题标题】:Detecting folders/directories in javascript FileList objects检测 javascript FileList 对象中的文件夹/目录
【发布时间】:2012-02-10 00:20:11
【问题描述】:

我最近向 Moodle 贡献了一些代码,它使用 HTML5 的一些功能允许通过从桌面拖放以表单形式上传文件(代码的核心部分在这里:https://github.com/moodle/moodle/blob/master/lib/form/dndupload.js 供参考) .

这很好用,除非用户拖动 文件夹/目录而不是真实文件。垃圾然后上传到服务器,但文件名与文件夹匹配。

我正在寻找一种简单可靠的方法来检测 FileList 对象中是否存在文件夹,这样我就可以跳过它(也可能返回友好的错误消息)。

我浏览了 MDN 上的文档以及更通用的网络搜索,但没有找到任何东西。我还查看了 Chrome 开发人员工具中的数据,似乎 File 对象的 'type' 始终设置为文件夹的 ""。但是,我不太相信这是最可靠的跨浏览器检测方法。

谁有更好的建议?

【问题讨论】:

    标签: html file-upload drag-and-drop moodle filelist


    【解决方案1】:

    你不能依赖file.type。没有扩展名的文件的类型为""。保存带有.jpg 扩展名的文本文件并将其加载到文件控件中,其类型将显示为image/jpeg。而且,名为“someFolder.jpg”的文件夹的类型也将是image/jpeg

    相反,尝试读取文件的第一个字节。如果您能够读取第一个字节,那么您就有了一个文件。如果抛出错误,你可能有一个目录:

    try {
        await file.slice(0, 1).arrayBuffer();
        // it's a file!
    }
    catch (err) {
        // it's a directory!
    }
    

    如果你处于支持 IE11 的不幸位置,该文​​件将没有arrayBuffer 方法。你必须求助于FileReader 对象:

    // use this code if you support IE11
    var reader = new FileReader();
    reader.onload = function (e) {
        // it's a file!
    };
    reader.onerror = function (e) {
        // it's a directory!
    };
    reader.readAsArrayBuffer(file.slice(0, 1));
    

    【讨论】:

    • 感谢您的建议 - 我想知道是否拒绝零大小的文件是一个很好的起点(这似乎是 Windows 上文件夹的大小),然后使用 FileReader 检查内容是否为 \当 File.size % 4096 为零时为 0
    • 当然。对我来说最令人惊讶的发现是文件夹的大小并非始终为 0。 % 4096 的事情很有趣。当然,需要进行更多的研究。比如,4096 是通用的,还是特定于 64 位 Windows 7?
    • 奇数 - 我在 Win 7(64 位)下始终获得大小 0(将仔细检查,但尝试使用 Chrome/Firefox)。但是,我使用 Ubuntu 11.10(64 位)(再次使用 Chrome/Firefox)获得了 4096 字节(确切地说)。
    • Do NOT 依赖 4096 或 0。在 Linux 上,它可以超过 4096,例如 8192 或更多,具体取决于文件夹包含的文件数量(或用于包含,因为删除文件不会减小大小)。在 Mac OS X 上,您可以使用各种小数字作为目录大小,例如 68、102、136、170,并且随着包含更多文件而增长。基本上大小不是你可以使用的。
    • 我建议调用reader.readAsText(file.slice(0,5) 以避免将大文件读入内存。
    【解决方案2】:

    我也遇到了这个问题,下面是我的解决方案。基本上,我采取了两管齐下的方法:

    (1) 检查 File 对象的大小是否很大,如果超过 1MB,则认为它是正版文件(我假设文件夹本身从来没有那么大)。 (2) 如果 File 对象小于 1MB,那么我使用 FileReader 的 'readAsArrayBuffer' 方法读取它。成功读取调用“onload”,我相信这表明文件对象是真正的文件。读取失败调用“onerror”,我认为它是一个目录。代码如下:

    var isLikelyFile = null;
    if (f.size > 1048576){ isLikelyFile = false; }
    else{
        var reader = new FileReader();
        reader.onload = function (result) { isLikelyFile = true; };
        reader.onerror = function(){ isLikelyFile = false; };
        reader.readAsArrayBuffer(f);
    }
    //wait for reader to finish : should be quick as file size is < 1MB ;-)
    var interval = setInterval(function() {
        if (isLikelyFile != null){
            clearInterval(interval);
            console.log('finished checking File object. isLikelyFile = ' + isLikelyFile);
        }
    }, 100);
    

    我在 FF 26、Chrome 31 和 Safari 6 中对此进行了测试,三个浏览器在尝试读取目录时调用了“onerror”。让我知道是否有人能想到一个失败的用例。

    【讨论】:

    • 好方法。除了带有 setInterval 的黑魔法,我发现它很容易出错。我最好使用异步回调系统,让读者以成功或失败结束。您的 readAsArrayBuffer() 也缺少参数。
    • readAsArrayBuffer 的成功之处 - 我更新了代码以反映错误修复(并修复另一个错误:如果文件大小 > 1 MB,isLikelyFile 现在为 false)。我没有遇到 setInterval 的问题,但请记住这一点。
    • 请注意,(f.size &gt; 1048576) 优化在某些情况下会出现问题,因为文件夹大小实际上可能比这更大。对于包含大量文件的文件夹,我检查了我的临时文件夹的大小(Windows 7),它是 2883584 字节...
    • 应该是:if (f.size > 1048576){ isLikelyFile = true; }
    • 这在 Firefox 94.0 中不起作用(对目录也执行 onload)
    【解决方案3】:

    我建议在 File 对象上调用 FileReader.readAsBinaryString。在 Firefox 中,当 FileDirectory 时,这将引发异常。我只有在File 满足gilly3 提出的条件时才这样做。

    更多详情请参阅我的博文http://hs2n.wordpress.com/2012/08/13/detecting-folders-in-html-drop-area/

    此外,Google Chrome 21 版现在支持删除文件夹。您可以轻松检查拖放的项目是否为文件夹,并读取其内容。

    很遗憾,我没有任何适用于旧版 Chrome 的(客户端)解决方案。

    【讨论】:

    • 请注意,gilly3 提出的尺寸标准无效,请参阅我在他的回答下的评论。
    【解决方案4】:

    另一个注意事项是,对于任何具有未知扩展名的文件,类型都是“”。尝试上传一个名为 test.blah 的文件,类型将为空。并且...尝试拖放名为 test.jpg 的文件夹 - 类型将设置为“image/jpeg”。要 100% 正确,您不能只依赖类型(或者完全依赖于类型)。

    在我的测试中,文件夹的大小始终为 0(在 FF 和 Chrome 上,64 位 Windows 7 和 Linux Mint 下(本质上是 Ubuntu)。所以,我的文件夹检查只是检查大小是否为 0,而且似乎在我们的环境中为我工作。我们也不希望上传 0 字节文件,因此如果它是 0 字节,则消息返回为“已跳过 - 0 字节(或文件夹)”

    【讨论】:

      【解决方案5】:

      仅供参考,这篇文章将告诉你如何在 Chrome 中使用 dataTransfer API 来检测文件类型:http://updates.html5rocks.com/2012/07/Drag-and-drop-a-folder-onto-Chrome-now-available

      【讨论】:

        【解决方案6】:

        最好的选择是在 FileReader 实例上同时使用 'progress' 和 'load' 事件。

        var fr = new FileReader();
        var type = '';
        
        // Early terminate reading files.
        fr.addEventListener('progress', function(e) {
            console.log('progress - valid file');
        
            fr.abort();
        
            type = 'file';
        });
        
        // The whole file loads before a progress event happens.
        fr.addEventListener('load', function(e) {
            console.log('load - valid file');
        
            type = 'file';
        });
        
        // Not a file.  Possibly a directory.
        fr.addEventListener('error', function(e) {
            console.log('error - not a file or is not readable by the web browser');
        });
        
        fr.readAsArrayBuffer(thefile);
        

        这会在出现目录时触发错误处理程序,大多数文件会在读取几 KB 后触发进度处理程序。我已经看到这两个事件都发生了。在进度处理程序中触发abort() 会阻止 FileReader 将更多数据从磁盘读取到 RAM 中。这允许删除非常大的文件,而无需将此类文件的所有数据读入 RAM 以确定它们是文件。

        如果发生错误,可能会说文件是一个目录。但是,存在许多 Web 浏览器无法读取文件的情况。最安全的做法是向用户报告错误并忽略该项目。

        【讨论】:

          【解决方案7】:

          一个简单的方法如下:

          1. 检查文件的type是否为空字符串:type === ""
          2. 检查文件的size是0、4096还是它的倍数:size % 4096 === 0
          if (file.type === "" && file.size % 4096 === 0) {
              // The file is a folder
          } else {
              // The file is not a folder
          }
          

          注意:可能会有一些没有文件扩展名的文件大小为 4096 的倍数。尽管这种情况不会经常发生,但请注意这一点。 p>


          有关参考,请参阅用户Marco Bonelligreat answer 到类似主题。这只是一个简短的总结。

          【讨论】:

            猜你喜欢
            • 2015-03-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-08-10
            • 2011-09-10
            • 2018-09-19
            • 2017-09-27
            相关资源
            最近更新 更多