javascript - How to upload and list directories at firefox and chrome/chromium using change and drop events -
both mozilla , webkit browsers allow directory upload. when directory or directories selected @ <input type="file"> element or dropped @ element, how list directories , files in order appear in actual directory @ both firefox , chrome/chromium, , perform tasks on files when uploaded directories have been iterated?
you can set webkitdirectory , allowdirs attributes @ <input type="file"> element; attach change, drop events <input type="file"> element; use .getfilesanddirectories() @ mozilla, .createreader(), .readentries() @ webkit, array.prototype.reduce(), promise, recursion.
note @ firefox drop event not list selection directory, file object having size 0, dropping directory @ firefox not provide representation of dropped folder, event.datatransfer.getfilesanddirectories() utilized. firefox provides 2 input elements when allowdirs attribute set; first element allows single file uploads, second element allows directory upload. chrome/chromium provide single <input type="file"> element single or multiple directories can selected, not single file.
at directories containing both files , directories, directories read first.
<!doctype html> <html> <head> <style type="text/css"> input[type="file"] { width: 98%; height: 180px; } label[for="file"] { width: 98%; height: 180px; } .area { display: block; border: 5px dotted #ccc; text-align: center; } .area:after { display: block; border: none; white-space: pre; content: "drop files or folders here!\aor click select files folders"; position: relative; left: 0%; top: -75px; text-align: center; } .drag { border: 5px dotted green; background-color: yellow; } #result ul { list-style: none; margin-top: 20px; } #result ul li { border-bottom: 1px solid #ccc; margin-bottom: 10px; } #result li span { font-weight: bold; color: navy; } </style> </head> <body> <label id="droparea" class="area"> <input id="file" type="file" directory allowdirs webkitdirectory/> </label> <output id="result"> <ul></ul> </output> <script> var droparea = document.getelementbyid("droparea"); var output = document.getelementbyid("result"); var ul = output.queryselector("ul"); function draghandler(event) { event.stoppropagation(); event.preventdefault(); droparea.classname = "area drag"; } function filesdroped(event) { var webkitresult = []; var mozresult = []; var files; console.log(event); event.stoppropagation(); event.preventdefault(); droparea.classname = "area"; // mozilla stuff // todo adjust, call `listdirectory()`, `listfile()` function mozreaddirectories(entries, path) { console.log("dir", entries, path); return [].reduce.call(entries, function(promise, entry) { return promise.then(function() { return promise.resolve(entry.getfilesanddirectories() || entry) .then(function(dir) { return dir }) }) }, promise.resolve()) .then(function(items) { var dir = items.filter(function(folder) { return folder instanceof directory }); var files = items.filter(function(file) { return file instanceof file }); if (files.length) { // console.log("files:", files, path); files.foreach(function(file) { console.log(file) }); mozresult = mozresult.concat.apply(mozresult, files); } if (dir.length) { // console.log(dir, dir[0] instanceof directory); return mozreaddirectories(dir, dir[0].path || path); } else { if (!dir.length) { return promise.resolve(mozresult).then(function(complete) { return complete }) } } }) }; function handleentries(entry) { let file = "webkitgetasentry" in entry ? entry.webkitgetasentry() : entry return promise.resolve(file); } function handlefile(entry) { return new promise(function(resolve) { if (entry.isfile) { entry.file(function(file) { listfile(file, entry.fullpath).then(resolve) }) } else if (entry.isdirectory) { var reader = entry.createreader(); reader.readentries(webkitreaddirectories.bind(null, entry, handlefile, resolve)) } else { var entries = [entry]; return entries.reduce(function(promise, file) { return promise.then(function() { return listdirectory(file) }) }, promise.resolve()) .then(function() { return promise.all(entries.map(function(file) { return listfile(file) })).then(resolve) }) } }) function webkitreaddirectories(entry, callback, resolve, entries) { console.log(entries); return listdirectory(entry).then(function(currentdirectory) { console.log(`iterating ${currentdirectory.name} directory`, entry); return entries.reduce(function(promise, directory) { return promise.then(function() { return callback(directory) }); }, promise.resolve()) }).then(resolve); } } // todo: handle mozilla directories, additional directories being selected dropped , listed without // creating nested list @ `html` different directory selected or dropped function listdirectory(entry) { console.log(entry); var path = (entry.fullpath || entry.webkitrelativepath.slice(0, entry.webkitrelativepath.lastindexof("/"))); var cname = path.split("/").filter(boolean).join("-"); console.log("cname", cname) if (!document.getelementsbyclassname(cname).length) { var directoryinfo = `<li><ul class=${cname}> <li> <span> directory name: ${entry.name}<br> path: ${path} <hr> </span> </li></ul></li>`; var curr = document.getelementsbytagname("ul"); var _ul = curr[curr.length - 1]; var _li = _ul.queryselectorall("li"); if (!document.queryselector("[class*=" + cname + "]")) { if (_li.length) { _li[_li.length - 1].innerhtml += `${directoryinfo}`; } else { _ul.innerhtml += `${directoryinfo}` } } else { ul.innerhtml += `${directoryinfo}` } } return promise.resolve(entry); } // todo: handle mozilla files function listfile(file, path) { path = path || file.webkitrelativepath || "/" + file.name; var filesinfo = `<li> name: ${file.name}</br> size: ${file.size} bytes</br> type: ${file.type}</br> modified date: ${file.lastmodifieddate}<br> full path: ${path} </li>`; var currentpath = path.split("/").filter(boolean); currentpath.pop(); var appended = false; var curr = document.getelementsbyclassname(`${currentpath.join("-")}`); if (curr.length) { (li of curr[curr.length - 1].queryselectorall("li")) { if (li.innerhtml.indexof(path.slice(0, path.lastindexof("/"))) > -1) { li.queryselector("span").insertadjacenthtml("afterend", `${filesinfo}`); appended = true; break; } } if (!appended) { curr[curr.length - 1].innerhtml += `${filesinfo}`; } } console.log(`reading ${file.name}, size: ${file.size}, path:${path}`); webkitresult.push(file); return promise.resolve(webkitresult) }; function processfiles(files) { promise.all([].map.call(files, function(file, index) { return handleentries(file, index).then(handlefile) })) .then(function() { console.log("complete", webkitresult) }) .catch(function(err) { alert(err.message); }) } if ("getfilesanddirectories" in event.target) { return (event.type === "drop" ? event.datatransfer : event.target).getfilesanddirectories() .then(function(dir) { if (dir[0] instanceof directory) { console.log(dir) return mozreaddirectories(dir, dir[0].path || path) .then(function(complete) { console.log("complete:", complete); event.target.value = null; }); } else { if (dir[0] instanceof file && dir[0].size > 0) { return promise.resolve(dir) .then(function(complete) { console.log("complete:", complete); }) } else { if (dir[0].size == 0) { throw new error("could not process '" + dir[0].name + "' directory" + " @ drop event @ firefox, upload folders @ 'choose folder...' input"); } } } }).catch(function(err) { alert(err) }) } // webkit stuff if (event.type === "drop" && event.target.webkitdirectory) { files = event.datatransfer.items || event.datatransfer.files; } else if (event.type === "change") { files = event.target.files; } if (files) { processfiles(files) } } droparea.addeventlistener("dragover", draghandler); droparea.addeventlistener("change", filesdroped); droparea.addeventlistener("drop", filesdroped); </script> </body> </html>
Comments
Post a Comment