3

A have a folder filled with files accessible to the end user, and am working on a javascript file to parse through them and deliver them as needed. However, rather than manually updating the list, I'd like the javascript to scan the folder and then list iterate through an array of the files in that folder. Is there a decent way in front-end JS to do this? All solutions I've looked into have turned out to be purely for Node.

For example, say I have a folder structure like so...

/ (Web Root)
|__ /Build_a_card
|__ /Cool pictures
  |__ /Summer Pictures
       summer_dog.gif
       smiling_sun.svg
  |__ /Winter Pictures
       snowman.png
  cat.jpg

And then in the javascript I'd run something like

var image_list = get_list("/Cool Pictures");
build_select_list(image_list);

function get_list(folder_to_look_in){
   var the_list = ???
   return the_list;
}
...

And then, for example, the JS is run, and after some parsing, the user would see...

<select>
  <option value="summer_pictures/summer_dog.gif">summer_dog.gif</option>
  <option value="summer_pictures/smiling_sun.svg">smiling_sun.svg</option>
  <option value="winter_pictures/snowman.png">snowman.png</option>
  <option value="cat.jpg">cat.jpg</option>
</select>

In an ridiculous world, since the individual files in the folder are accessible to javascript, hypothetically I could brute-force every single possible file name in the folder and return success on each one:

function get_list(folder){
  var list_of_files = {};
  var starting_character = 0;
     list_of_files = every_single_option({starting_character}, 0, 40, folder)
  }
}
function every_single_option(existing_characters, current_depth, max_depth, folder){
  this_string = String.fromCharCode(existing_characters);
  if (request_url(this_string, folder)){
     there_array[this_string] = this_string;
  }
  var there_array = {}
  var i;
  if (current_depth < max_depth){
    while (i < 127){
        let temp_array = there_array;
        temp_array[i] = i;
        mix_source(there_array, every_single_option(existing_characters, current_depth + 1, max_depth, folder))
    }
}
return there_array;
}
function request_url(url, folder){
  var oReq = new XMLHttpRequest();
  oReq.addEventListener("load", reqListener);
  oReq.open("GET", "/" + folder + "/" + url);
  oReq.send();
}
function mix(source, target) {
   for(var key in source) {
     if (source.hasOwnProperty(key)) {
        target[key] = source[key];
     }
   }
}

but as mentioned, doing it that way would be ridiculous (both unusably slow and very bad code design, resorting to brute-forcing your own website is just dumb.)

but it does hypothetically prove that there's no reason javascript shouldn't be able to just get a directory listing assuming public permissions. Alternatively, I could make some API with the backend that allows fetching a JSON that lists it, but that's requiring backend code for something that's a frontend process. I'm trying to pull this off with something sane and simple, but the question is... how?

(If you insist on posting a jquery way to do this, please also post a non-jquery way as well as there is no jquery available in my environment.)

lilHar
  • 1,735
  • 3
  • 21
  • 35
  • 3
    Unfortunately You cannot operate with file structure of backend, cause Your frontend JS code cannot access to server's file system (understand it as security concerns). Yes, You can write API and get them. But since You want to avoid backend API - there is a solution: **You have to provide some file that will represent folder tree (`.tree.json`). This file must be generated every time when something changes on dedicated folder. Then develop that `get_list` method that will read `.tree.json` and provide necessary feature.** – num8er Feb 25 '19 at 00:02
  • 1
    Btw how about: AWS S3 or Firebase to keep files there and get them using their API? – num8er Feb 25 '19 at 00:04
  • Both decent thoughts, but I'm trying to avoid back-end shenanigans. – lilHar Feb 25 '19 at 00:05
  • 1
    If Your file listing not changing often You can have pregenerated listing file and load it at page load. Otherwise... maybe Apache or Nginx have plugins to give listing? `mod_dir` in Apache https://stackoverflow.com/questions/20448339/apache-directory-listing-as-json – num8er Feb 25 '19 at 00:07
  • 1
    Nginx has it by default: https://stackoverflow.com/questions/24387118/nginx-list-static-files-directories-as-xml-json – num8er Feb 25 '19 at 00:09
  • 1
    All good ideas for some people (especially those who may google their way here), but still all requiring backend specifics.Me personally, I'm trying to make a library for what I'm doing that will be able to be moved between a few different systems, hence the focus on doing it front-end. – lilHar Feb 25 '19 at 00:12
  • @liljoshu — As num8er said, that's impossible. – Quentin Feb 25 '19 at 16:57
  • @Quentin Considering I have a solution in my answer that "works" (albeit it's insane) in my question, that proves it's not impossible. I have a possible solution (the worst possible solution, really) and I'm looking for a better solution. – lilHar Feb 25 '19 at 17:00
  • 1
    For a given value of "works". That's so inefficient that I wouldn't expect it to qualify. – Quentin Feb 25 '19 at 17:00
  • Exactly why I want something better. Really, all that bad solution does is prove it's possible. And since it's merely testing permission, it doesn't even flag most security tests either assuming it's not running hot enough to trigger a DDOS security protocol, which means not even 'security' isn't a valid reason for javascript to not be able to do this. – lilHar Feb 25 '19 at 17:04

3 Answers3

6

So, refusing to admit it's impossible, I engineered a solution that works, and requires no API. That said, the server has to not be actively blocking the javascript from viewing the directory. In other words, the server hasn't turned indexing off, and the directory doesn't have an index.html or equivalent to rewrite any attempt to index, and the server isn't doing some url-rewriting. In other words, this should work in any server environment that doesn't rewrite or block indexes.

Here's a rough draft (still buggy, needs finished):

var request = new XMLHttpRequest();
request.open('GET', '/my/directory/', true);

request.onload = function() {
  if (request.status >= 200 && request.status < 400) {
    // Success!
    var resp = request.responseText;
  }
};

request.send();
var directory_listing = resp;
var regexp = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i;
var match, files = [];

while ((match = regexp.exec(resp)) != null) {
  files.push(match.index);
}

console.log(files);
lilHar
  • 1,735
  • 3
  • 21
  • 35
1

Building off lilHar's answer, we can use DOMParser to create a shadow-DOM for the directory page we're accessing, and then use that to find any links we need:

// relative path to the desired directory
const directory = "/DIRECTORY-NAME/";

// selector for the relevant links in the directory's index page
const selector = "LINK SELECTOR";

const request = new XMLHttpRequest();
request.open("GET", directory, true);

request.onload = () => {
    // succesful response
    if(request.status >= 200 && request.status < 400)
    {
        // create DOM from response HTML
        const doc = new DOMParser().parseFromString(request.responseText, "text/html");

        // get all links
        const links = doc.querySelectorAll(selector);
        console.log("Links:", links);

        links.forEach(link => {
            // do stuff with the links
        });
    }
};

request.send();
Tommy
  • 95
  • 1
  • 9
  • I like that better, it's a lot cleaner with less reliance on regex! (Less regex is always better! :D ) – lilHar Jun 27 '22 at 17:53
-1

Is there a decent way in front-end JS to do this?

No. Nor is that a way that isn't decent.

The front end can communicate with the server via HTTP or WebSockets.

Neither of those provides any built-in mechanism for exploring a filesystem.

You need the server to provide an API (e.g. a web service) which provides the information you want.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335