检测 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 > 1048576)
优化在某些情况下会出现问题,因为文件夹大小实际上可能比这更大。对于包含大量文件的文件夹,我检查了我的临时文件夹的大小(Windows 7),它是 2883584 字节...
应该是:if (f.size > 1048576) isLikelyFile = true;
这在 Firefox 94.0 中不起作用(对目录也执行 onload)【参考方案3】:
我建议在 File
对象上调用 FileReader.readAsBinaryString
。在 Firefox 中,当 File
是 Directory
时,这将引发异常。我只有在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 集合中?