检测 javascript FileList 对象中的文件夹/目录

Posted

技术标签:

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

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

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

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

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

谁有更好的建议?

【问题讨论】:

【参考方案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】:

一个简单的方法如下:

    检查文件的type是否为空字符串:type === "" 检查文件的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 Bonelli 的great answer 到类似主题。这只是一个简短的总结。

【讨论】:

以上是关于检测 javascript FileList 对象中的文件夹/目录的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaScript 中将 File 对象添加到 FileList 集合中?

在没有表单输入的情况下对 FileList 数据进行排序

是否可以组合两个 FileList 对象?

file控件,以及fileList对象。

如何在 FileList 对象中设置文件对象和长度属性,其中文件也反映在 FormData 对象中?

JavaScript中 FileReader 对象详解