我最近为Moodle贡献了一些代码,它使用HTML5的一些功能,允许通过桌面拖放将文件上传到表单中(代码的核心部分在这里:https://github.com/moodle/moodle/blob/master/lib/form/dndupload.js for参考)。
除非用户拖动 文件夹/目录而不是真实文件,否则此功能正常。然后将垃圾上传到服务器,但文件名与文件夹匹配。
我正在寻找的是检测 FileList 对象中文件夹的简单可靠方式,所以我可以跳过它(也可能返回友好的错误消息)。
我查看了MDN上的文档,以及更一般的网络搜索,但没有透露任何内容。我还浏览了Chrome开发人员工具中的数据,看来文件对象的'type'始终设置为文件夹的“”。但是,我不太相信这是最可靠的跨浏览器检测方法。
有没有人有更好的建议?
答案 0 :(得分:19)
您不能依赖file.type
。没有扩展名的文件的类型为""
。保存带有.jpg
扩展名的文本文件并将其加载到文件控件中,其类型将显示为image/jpeg
。并且,名为“someFolder.jpg”的文件夹的类型也将为image/jpeg
。
尝试使用FileReader
读取文件。如果拖入目录,FileReader
将引发error
事件:
var reader = new FileReader();
reader.onload = function (e) {
// it's a file
};
reader.onerror = function (e) {
// it's a directory
};
reader.readAsText(file);
令人高兴的是,在IE11中,当删除目录时,e.dataTransfer.files
集合为空。
Chrome在名为e.dataTransfer
的{{1}}中公开了一个包含items
个对象集合的附加属性。在每个对象上,您可以调用DataTransferItem
,它返回item.webkitGetAsEntry()
个对象。 Entry
对象具有属性Entry
和isDirectory
:
isFile
有趣的是,在我的实验中,我看过的每个文件夹的// Chrome only
if (e.dataTransfer.items && e.dataTransfer.items.length) {
[].forEach.call(e.dataTransfer.items, function(item) {
var entry = item.webkitGetAsEntry();
if (entry && entry.isFile) {
var file = item.getAsFile(); // same as object in e.dataTransfer.files[]
// do something with the file
}
}
}
都为零。但是,当然,一些文件也会有。 File.size % 4096
不是文件是否实际上是文件夹的可靠指标。
答案 1 :(得分:6)
我也遇到了这个问题,下面是我的解决方案。基本上,我采取了双管齐下的方法:
(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”。如果有人能想到失败的用例,请告诉我。
答案 2 :(得分:2)
我建议在FileReader.readAsBinaryString
对象上调用File
。在Firefox中,当File
为Directory
时,这会引发异常。如果File
符合gilly3提出的条件,我才会这样做。
有关详细信息,请参阅http://hs2n.wordpress.com/2012/08/13/detecting-folders-in-html-drop-area/上的博文。
此外,Google Chrome的第21版现在支持删除文件夹。您可以轻松检查已删除的项目是否为文件夹,还可以阅读其内容。
不幸的是,我没有任何(客户端)解决方案适用于较旧的Chrome版本。
答案 3 :(得分:0)
另一个注意事项是,对于任何具有未知扩展名的文件,类型为“”。尝试上传名为test.blah的文件,类型将为空。 AND ...尝试拖放名为test.jpg的文件夹 - 将设置为“image / jpeg”。要100%正确,你不能单独依赖类型(或者如果真的那样)。
在我的测试中,文件夹的大小一直是0(在64位Windows 7和Linux Mint(基本上是Ubuntu)的FF和Chrome上。所以,我的文件夹检查只是检查大小是否为0并且它似乎在我们的环境中为我工作。我们也不希望上传0字节文件,如果它是0字节,则消息返回为“已跳过 - 0字节(或文件夹)”
答案 4 :(得分:0)
仅供参考,本文将告诉您如何在Chrome中使用dataTransfer API来检测文件类型:http://updates.html5rocks.com/2012/07/Drag-and-drop-a-folder-onto-Chrome-now-available
答案 5 :(得分:0)
最好的选择是在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只是为了确定它们是文件。
很可能会说,如果发生错误,则File为目录。但是,存在许多方案,其中Web浏览器无法读取该文件。仅向用户报告错误并忽略该项目是最安全的。