Chat freely about anything...

User avatar
By treii28
#53766 Following the example in the SD webserver code, I modified the printDirectory command to call out to a separate function to get the contents of whatever directory was being requested. I did this so I could set the second function up as a recursive call to dump out the entire directory tree of the SD card. If I request it directly, I see output streaming out of the esp slowly, but when I try to call it from a jquery ajax call, it never seems to complete. Or at list it never seems to evoke the function call.

I even tried adding server.close() to the end of each to see if I could force it to say it's done. Still no go....

It was working from jquery with the single function in the example code, but I can't seem to get it to work now that I broke it up. What am I missing?

Code: Select all// should only be called as part of another print function that already sent a header
void listDirJSON(String path, boolean descend) {
  server.sendContent("[");

  File dir = SD.open((char *)path.c_str());
  dir.rewindDirectory();
  for (int cnt = 0; true; ++cnt) {
    File entry = dir.openNextFile();
    if (!entry)
      break;

    String filename = entry.name();
    // skip dot files
    if (filename.startsWith("."))
      break;

    String output = (cnt > 0) ? "," : "";
    output += "{name:\"";
    output += filename;
    output += "\",";
    server.sendContent(output);

    if (entry.isDirectory()) {
      server.sendContent("type:\"dir\"");
      if (descend == true) {
        server.sendContent(",content:");

        String fullpath = path + filename + "/";
        listDirJSON(fullpath, descend);
      }
    } else {
      output = "type:\"file\",size:";
      output += String(entry.size(), DEC);
      server.sendContent(output);
    }

    server.sendContent("}");
    entry.close();
  }
  dir.close();
  server.sendContent("]");
}

void printFS() {
  server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  server.send(200, "application/json", "");
  WiFiClient client = server.client();

  server.sendContent("{name:\"/\",type:\"dir\",content:");
  listDirJSON("/", true);
  server.sendContent("}");
  server.close();
}

void printDirectory() {
  String path = getDirArg();
  if (serverError != "") {
    returnFail(serverError);
    serverError = "";
    return;
  }

  server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  server.send(200, "application/json", "");

  String output = "{name:\"";
  output += path;
  output += "\",type:\"dir\",content:";
  server.sendContent(output);

  listDirJSON(path, false);

  server.sendContent("}");
  server.close();
}


example of my ajax jquery call:

Code: Select all            console.log("making ajax call"); // I see this
            $.ajax({
                dataType: "json",
                url: "/list",
                data: { dir:"/"},
                success: function(data) {
                    console.log("content loaded"); // I never see this
                    console.log(data);
                }
            });


EDIT: Initially I set it up to build and return a string of the entire json data but the string was getting huge so converted it back to streaming. But I missed three instances of my trying to do the string handling. One on the declaration of the recurring json sub-function and one each where it was called. I edited those back out in the code and above and still getting the same result.
User avatar
By treii28
#53787
martinayotte wrote:For sure, doing many sendContent() calls is the issue.
You really need to go back to your first try of building a single string and single sendContent().


I'm trying another approach and it seems to work as far as making things a whole lot faster, but still never seems to complete which seriously has me scratching my head. Since the scanning of the entire card was a bit slow when combining it with the i/o streaming, I figured maybe writing to a temporary file then streaming the file itself might be better as I could add an additional url parameter and a SD.exists check to trigger the actual scan, otherwise just cache the results. That works great and is really fast - but the javascript still doesn't seem to work.

The issue on that one is just my lack of understanding with C++. I tried passing the File handle to the recursive routine as a reference but then it gave me an error when I was trying to call it's various methods. I'm still blurry on the syntax for the references and pointers and not even sure when doing the recursion if I pass the variable as is or again try to reference it? (it's already a pointer to the object at that point)

I solved it in the short term by just making the file handle a global to get it working, but I would prefer to pass the file handle by reference. Worst case scenario is I can try going back to what I vaguely recall from college and try passing an ofstream, but I'm not sure how well that would behave with the Arduino File object.

So what is the proper syntax to pass the File object to a recursive function, then to access the print method (the error it gave me was asking if maybe I meant '->' or something like that?)
User avatar
By martinayotte
#53804 I don't understand why you would need streaming and why are you talking about recursion ...

A simple directory listing can easily fit in a simple string, it is not so huge to require other method.
Here is a piceceof code that list the files present in SPIFFS, same can be done with SD :
Code: Select allvoid handleDirList() {
  String str = String("<html><head></head><body background=\"/metalic.png\">\r\n");
  str += "<h2>---- SPIFFS Dir List ----</h2><br>\r\n";
  if (!SPIFFS.begin()) {
    Serial.println("SPIFFS failed to mount !\r\n");
  }
  Dir dir = SPIFFS.openDir("/");
  while (dir.next()) {
    str += "<a href=\"view?file=";
    str += dir.fileName();
    str += "\">";
    str += dir.fileName();
    str += "</a> - ";
    str += dir.fileSize();
    str += "<br>\r\n";
  }
  str += "</body></html>\r\n";
  webserver.send(200, "text/html", str);
}