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

时间:2012-01-13 20:09:23

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

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

除非用户拖动 文件夹/目录而不是真实文件,否则此功能正常。然后将垃圾上传到服务器,但文件名与文件夹匹配。

我正在寻找的是检测 FileList 对象中文件夹的简单可靠方式,所以我可以跳过它(也可能返回友好的错误消息)。

我查看了MDN上的文档,以及更一般的网络搜索,但没有透露任何内容。我还浏览了Chrome开发人员工具中的数据,看来文件对象的'type'始终设置为文件夹的“”。但是,我不太相信这是最可靠的跨浏览器检测方法。

有没有人有更好的建议?

6 个答案:

答案 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对象具有属性EntryisDirectory

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中,当FileDirectory时,这会引发异常。如果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浏览器无法读取该文件。仅向用户报告错误并忽略该项目是最安全的。