【问题标题】:How to distinguish if a file or folder is being dragged prior to it being dropped?如何区分文件或文件夹是否在被拖放之前被拖动?
【发布时间】:2014-09-20 21:24:22
【问题描述】:

我正在尝试检测是否在 dragoverdragenter 事件中拖动了文件夹或文件。

例如:

ondrop 事件中,有一个名为MouseEvent 的参数,它有一个名为dataTransfer 的字段,其中列出了文件(.files)或项目(.items),具体取决于浏览器,我可以在ChromeFirefox 中读到这一点。但是,对于 dragoverdragenter 事件,这些字段(.files.items)为空。问题是我在拖动而不是拖放时需要这些信息

注意:对于文件和文件夹,event.dataTransfer.types[i] === "Files"true

背景研究

我发现the following answer 部分适合我的问题:

WebKit 以及 Chrome 对您何时可以调用 getData 有很大的限制。您不能在 dragstartdragover 内进行操作。我认为这是典型的错误。

但这个答案是 2012 年的,我找不到关于该主题的实际更新信息,所以我正在寻找这方面的更新信息。

【问题讨论】:

  • 我在这里找到了适合我的问题的答案 (stackoverflow.com/questions/9534677/…) 但现在是 2012 年,我找不到实际信息(
  • 澄清或演示问题的最小工作示例可能有助于人们提供高质量的答案。
  • 我的猜测是您根本无法做到这一点:如果我在浏览器窗口上将文件或文件夹拖到不相关的地方怎么办:我不希望随机网站收集有关如果它不是最终目标,我正在拖动的内容。
  • @KristinaKurshakova 你应该做正确的事并接受下面的答案。 Marco 提供了高质量的答案。

标签: javascript html drag-and-drop directory


【解决方案1】:

TL;DR 你不能。

如果您想知道为什么这个问题仍然没有得到接受的答案,您可以阅读由 OP 创建的 this meta questionmy answer

HTML5 中的文件drag/drop

我对这个主题的不同文档进行了一些研究,并在各种浏览器上进行了自己的测试,所以我决定在这里总结一下我对拖放文件的了解。

拖动

拖动文件时可以使用一些监听器,例如:

  • dragenter
  • dragover
  • dragend
  • dragleave

鉴于这些是 drag 事件,event.dataTransferfiles 属性将具有 length == 0 或为空 (null)。

无法在拖动事件中读取文件详细信息,并且您无法检查它们是否为文件夹。这不是错误,而是一项安全功能。

想象一下,您可以在拖动事件中读取文件:即使用户不想将文件上传到您的站点,您也可以读取所有内容。说真的,这没有任何意义。想象一下,您正在将文件从桌面拖到另一个文件夹,并且您不小心将其拖过网页:现在网页读取您的文件并将您的个人信息存储在其服务器上...... 这将是一个巨大的安全漏洞.

但是,您仍然可以通过遍历数组event.dataTransfer.types 来检测用户是否正在拖动文件(文件我也指文件夹,因为文件夹也是文件)。您可以创建一个函数来检查拖动事件是否包含文件,然后在事件处理程序中调用它。

例子:

function containsFiles(event) {
    if (event.dataTransfer.types) {
        for (var i=0; i<event.dataTransfer.types.length; i++) {
            if (event.dataTransfer.types[i] == "Files") {
                return true;
            }
        }
    }
    
    return false;
}

function handleDragEnter(e) {
    e.preventDefault();
    if (containsFiles(e)) {
        // The drag event contains files
        // Do something
    } else {
        // The drag event doesn't contain files
        // Do something else
    }
}

掉落

当您将文件拖放到 drop &lt;div&gt;(或您用作 dropzone 的任何元素)中时,您将使用事件 drop 的侦听器来读取一些文件属性,例如名称、大小、类型和最后修改日期。

要检测文件是否为文件夹,您将:

  1. 检查文件是否有type == "",因为文件夹没有类型。
  2. 检查文件大小是否为 4096 的倍数:size%4096 == 0,因为文件夹的大小始终是 4096 字节(即 4KiB)的倍数。

例子:

function handleDrop(e) {
    e.stopPropagation();
    e.preventDefault();

    var files = e.dataTransfer.files;

    for (var i = 0, f; f = files[i]; i++) { // iterate in the files dropped
        if (!f.type && f.size%4096 == 0) {
            // The file is a folder
            // Do something
        } else {
            // The file is not a folder
            // Do something else
        }
    }
}

已知问题:由于文件夹实际上是文件,因此这是将它们与其他文件区分开来的唯一方法。尽管此方法不能绝对确定文件是文件夹:它可能是没有扩展名且大小为 0 或正好为 N x 4096B 的文件。


工作示例

这里有一些工作示例,以查看我上面所说的内容并自行测试。在运行它们之前,请确保您的浏览器 supports drag and drop features.玩得开心:

【讨论】:

  • 答案已编辑。这是我能做的最好的。很抱歉,您似乎无法读取 drag 事件中的文件。
  • 我知道。我对您的问题的回答是,您确实无法检查文件是否是拖动事件中的文件夹。对不起。
  • +1 你有关于文件夹检测的两个陈述的来源吗?我不认为任何规范都可以保证文件夹大小。这是一个启发式价值,不是吗?
  • 系统以 4KiB 为单位表示文件夹,是的,这实际上是一个启发式值,没有官方的方法来区分文件和文件夹。
  • 在 Chrome 39.0.2171.99 上查看本地文件,您现在可以查看dataTransfer 的拖动事件,特别是ondragenterondragover
【解决方案2】:

这适用于 Dropping -on drop event-(请注意,这不适用于 dragover 事件):

isDraggedItemIsFile = function(e) {
// handle FF
if (e.originalEvent.dataTransfer.files.length == 0) {
    return false;
}
// handle Chrome
if (e.originalEvent.dataTransfer.items) {
    if (typeof (e.originalEvent.dataTransfer.items[0].webkitGetAsEntry) == "function") {
        return e.originalEvent.dataTransfer.items[0].webkitGetAsEntry().isFile;
    } else if (typeof (e.originalEvent.dataTransfer.items[0].getAsEntry) == "function") {
        return e.originalEvent.dataTransfer.items[0].getAsEntry().isFile;
    }
}
return true;
};

$forms.on('drop', function(e) {
        if (isDraggedItemIsFile(e)) {
            // do something if file
        } else{
           // is directory
        }
    });

在 FF V49、Chrome V55、Edge V25 上测试

【讨论】:

  • 正是 OP 在寻找的东西。不正确、不相关、容易混淆、特定于浏览器和基于 jQuery。
  • 有没有办法在 Safari 上做同样的事情?
【解决方案3】:

我能够在我的页面上获得被拖动的东西的整个 Mimetype。文件夹的 Mimetype 似乎是空白的,所以也许你可以这样区分它。

部分代码(从 React 中提取):

function handleDragOver(ev: DragEvent) {
    ev.preventDefault();
    ev.dataTransfer!.dropEffect = 'copy';
    console.log(Array.from(ev.dataTransfer.items).map(i => [i.kind,i.type].join('|')).join(', '));
}

document.addEventListener('dragover',handleDragOver);

输出如下:

file|image/x-icon, file|image/jpeg, file|application/vnd.ms-excel

当我在页面上拖动 3 个文件时。

不确定它是否仅适用于本地主机,我还没有上传到任何地方,但它完全可以工作。

MDN docs on DataTransferItem

【讨论】:

  • 适用于 Chrome 80 windows 10 而不是 localhost jsfiddle.net/greggman/rcwzeu2t 但操作系统不知道 mime 类型的任何文件都显示为无 mime 类型,因此无法区分文件夹和文件跨度>
【解决方案4】:

您可以使用 FileReaderwebkitGetAsEntry()

将文件与文件夹分开

ie11 不支持 webkitGetAsEntry(),请记住这一点!

代码如下:

 onDrop(event) {
    let files = event.dataTransfer ? event.dataTransfer.files : 'null';

    for(let i = 0, file; file = files[i]; i++) {
       var reader = new FileReader();

       reader.onload = function (e) {
           console.log('it is a file!');
       };
       reader.onerror = function (e) {
          console.log('it is a folder!');
       };

       reader.readAsText(file);
    }

}

【讨论】:

  • 我认为您可能错过了描述他在拖动和拖动输入期间如何需要此信息的部分问题,在这两个事件期间对“webkitGetAsEntry()”的调用返回 null。跨度>
  • 但还是很有帮助的,因为现在我知道如何识别真实文件了。我将在我的上传器组件中使用它,非常感谢; )
猜你喜欢
  • 2012-11-10
  • 1970-01-01
  • 1970-01-01
  • 2011-04-05
  • 2020-02-10
  • 1970-01-01
  • 2013-12-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多