htmlfile-uploaddrag-and-dropmoodlefilelist

Detecting folders/directories in javascript FileList objects


I have recently contributed some code to Moodle which uses some of the capabilities of HTML5 to allow files to be uploaded in forms via drag and drop from the desktop (the core part of the code is here: https://github.com/moodle/moodle/blob/master/lib/form/dndupload.js for reference).

This is working well, except for when a user drags a folder / directory instead of a real file. Garbage is then uploaded to the server, but with the filename matching the folder.

What I am looking for is an easy and reliable way to detect the presence of a folder in the FileList object, so I can skip it (and probably return a friendly error message as well).

I've looked through the documentation on MDN, as well as a more general web search, but not turned up anything. I've also looked through the data in the Chrome developer tools and it appears that the 'type' of the File object is consistently set to "" for folders. However, I'm not quite convinced this is the most reliable, cross-browser detection method.

Does anyone have any better suggestions?


Solution

  • You cannot rely on file.type. A file without an extension will have a type of "". Save a text file with a .jpg extension and load it into a file control, and its type will display as image/jpeg. And, a folder named "someFolder.jpg" will also have its type as image/jpeg.

    Instead, try to read the first byte of the file. If you are able to read the first byte, you have a file. If an error is thrown, you probably have a directory:

    try {
        await file.slice(0, 1).arrayBuffer();
        // it's a file!
    }
    catch (err) {
        // it's a directory!
    }
    

    If you are in the unfortunate position of supporting IE11, The file will not have the arrayBuffer method. You have to resort to the FileReader object:

    // 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));