user81993
user81993

Reputation: 6613

How to tell whether a dropped file is a folder or not?

I have an element that allows for dropping things on it like so

var div = document.createElement("div");
div.addEventListener("drop", (e)=>{
    e.preventDefault();
    if (!(("dataTransfer" in e) && ("files" in e.dataTransfer))) return;
    for (var i = 0; i < e.dataTransfer.files.length; i++) {
        var file = e.dataTransfer.files[i];
        //    magical check whether the file is a folder?
        upload_file(file);
    }
});

Problem is, folders show up in the FileList object just the same and they seem to be indisginguishable from files up until the point where you try to read them using FileReader.

They have a size which seems to be exactly the cluster size of whatever drive the file is from, they have no type but same is true for any uncommon file extension. When you try to read them with FileReader, the onload even is simply never raised.

I could try and do a test read with some kind of timeout mechanism in place to detect that its not a file but this seems like a dirty method bound to result in errors in any future browser update.

Is there any other way to figure out that the file provided is not in fact a file?

Upvotes: 3

Views: 995

Answers (2)

Filippo Possenti
Filippo Possenti

Reputation: 1410

I stumbled upon an answer in another StackOverflow discussion that might be useful in relation to this.

Detecting folders/directories in javascript FileList objects

Basically you can detect if it's a file or a directory "indirectly" by seeing if reading it throws an exception. A snippet follows:

<html>
<head>
    <title></title>
</head>
<body>
    <div dropzone="copy" id="dropTarget" ondrop="handleDrop(event);" ondragover="event.preventDefault();" style="border: 1px solid gray; width: 300px; height: 50px;">
        Drop your folder here
    </div>
    <script type="text/javascript">
        var item = null;
        var handle = null;
        var file = null;
        function handleDrop(e) {
            e.preventDefault();
            item = e.dataTransfer.items[0];
            file = item.getAsFile();
            file.slice(0,1).arrayBuffer().then(buf => console.log("file"), err => console.log("dir"));
            item.getAsFileSystemHandle().then(h => { handle = h; console.log("done"); });
        };
    </script>
</body>

Upvotes: 1

user81993
user81993

Reputation: 6613

Searching far and wide, the only real option I found was using an experimental feature supported by edge, chrome and ff

For my implementation, before rendering the page, I do this test to check whether or not to allow for drops at all:

browser_support_drop() {
    if (typeof DragEvent !== "function") return false;
    if (!("dataTransfer" in DragEvent.prototype)) return false;
    if (typeof DataTransferItem !== "function") return false;
    if (!("webkitGetAsEntry" in DataTransferItem.prototype) && !("getAsEntry" in DataTransferItem.prototype)) return false;
    return true;
}

and then in the drop handler (if applicable)

handle_drop(e) {
    var items = [];
    for (var i = 0; i < e.dataTransfer.items.length; i++) {
        var item = e.dataTransfer.items[i];
        if (item.kind !== "file") continue;
        var entry = "getAsEntry" in DataTransferItem.prototype ? item.getAsEntry() : item.webkitGetAsEntry();
        if (entry.isDirectory) continue;
        items.push(item.getAsFile());
    }
    this.handle_files_added(items);
}

Upvotes: 1

Related Questions