Good Afternoon,
Working on the RainGauge Log page; trying to make file name a variable in the server unc path. Issue I am running into is a hard coded path works:
“<div><object data=http://xx.xxx.xxx.xxx:yyyy/LOG06242021.TXT width=”100%” height=”700″></object></div>”
One with a placeholder does not load the text file:
“<div><object data=http://xx.xxx.xxx.xxx:yyyy/%UNCFB% width=”100%” height=”700″></object></div>”
William
Found my mistake; placed placeholders in the wrong String processor (Code has six String processors.)
Server unc path with placeholders:
“<div><object data=http://%LINK%/%UNCFN% width=”100%” height=”700″></object></div>”
Have been working on adding a hyperlink to displayed log file. Only have it working in a round about way; first select “File Browser” to list all Log files, then selc option “Show Log File” will display Log file again. This time the displayed displayed Log file will have a hyperlink to “SdBrowse.”
Variable for filename is extracted when the “SdBrowse” can not find the filename; using the “notFound” function the filename gets “retrieved” making the filename avaiable for the “Show Log File” option.
ESP32 Asyncwebserver project website
Looking for way to go directly to “Show Log File;” from notFound function.
“request->send(SPIFFS, fn, String(), false);” is used to display Log file.
Features of the ESP32 RainGauge Project
Show Log File:
serverAsync.on(“/Show”, HTTP_GET, [](AsyncWebServerRequest * request)
{
PATH = “/Show”;
accessLog();
if (! flag == 1)
{
request->send_P(200, PSTR(“text/html”), HTML6, processor6);
}
end();
});
William
Hi William.
I’m glad you found the issue.
Is there anything else that I can help you with?
Regards,
Sara
Good Morning Sara,
Looking for way to go directly to “Show Log File;” from notFound function.
notFound function:
String notFound(AsyncWebServerRequest *request)
{
digitalWrite(online, HIGH); //turn-on online LED indicator
if (! request->url().endsWith(F(“.TXT”)))
{
request->send(404);
}
else
{
if (request->url().endsWith(F(“.TXT”)))
{
//.endsWith(F(“.txt”)))
// here comes some mambo-jambo to extract the filename from request->url()
int fnsstart = request->url().lastIndexOf(‘/’);
fn = request->url().substring(fnsstart);
uncfn = fn.substring(1);
PATH = fn;
//accessLog();
File webFile = SPIFFS.open(fn);
Serial.print(“File size: “);
Serial.println(webFile.size());
if (!webFile)
{
Serial.println(“File: ” + fn + ” failed to open”);
Serial.println(“\n”);
}
else if (webFile.size() == 0)
{
webFile.close();
}
else
{
//request->send(SPIFFS, fn, String(), true); //Download file
request->send(SPIFFS, fn, String(), false); //Display file
//This what I am trying to do… without a GET request.
//request->send_P(200, PSTR(“text/html”), HTML6, processor6);
//When I try this I see “Collected Observations” and “SdBrowse” from index6.h (HTML) wirh no text.
webFile.close();
}
fn = “”;
end();
}
}
digitalWrite(online, LOW); //turn-off online LED indicator
return(uncfn);
}
William
Good Morning,
Clarification: notFound fuction is not able to do what I am trying to accomplish.
//From inside of the notFound function:
//This what I am trying to do… without a GET request.
//request->send_P(200, PSTR(“text/html”), HTML6, processor6);
This what I need to send in the notFound function:
//index6.h
const char HTML6[] PROGMEM = R”====(
<!DOCTYPE HTML>
<html>
<head>
<title>Observations</title>
</head>
<body>
<h2>Collected Observations
<br>
<br> <div><object data=http://%LINK%/%UNCFN% width=”100%” height=”700″></object></div>
<br>
<br>
<a href=http://%LINK%/SdBrowse >SdBrowse</a></h2><br>
</body>
</html>
)====”;
William
Hi William.
Thanks for the clarification. Now, I think I understood what you are trying to do.
You want to send a file when the notFound function is triggered.
Here’s something that you need to keep in mind: with HTTP requests, you can only send a response if you are requested something.
If you want to send information from the server to the client without being requested, you can use server-sent events or WebSocket protocol. However, at the moment, I don’t have any tutorials about sending files via these protocols. But, it should be possible to implement.
Have you taken a look at the notFound() function implementation in the AsyncWebServer documentation: https://github.com/me-no-dev/ESPAsyncWebServer
In what scenario should that function run?
What happens when you run the command you’ve shown before?
//request->send_P(200, PSTR(“text/html”), HTML6, processor6);
Regards,
Sara
Thank you Sara; I have read documentation; just not sure I have a full grasp of all the details, also have gone thru the open and closed issues. Posted my issue on the github library page.
request->send_P(200, PSTR(“text/html”), HTML6, processor6);
Other two requests in the “notFound” fuction commented out; produces a web page with header and hyperlink and a mirror of the header and hyperlink, but no text. I beleve this is due to not having the filename returned at this point in the “notFound” function.
Here is what I have so far:
Start your code here
serverAsync.on(“/SdBrowse”, HTTP_GET, [](AsyncWebServerRequest * request)
{
PATH = “/SdBrowse”;
accessLog();
if (! flag == 1)
{
request->send_P(200, PSTR(“text/html”), HTML2, processor2);
}
end();
});
serverAsync.on(“/Show”, HTTP_GET, [](AsyncWebServerRequest * request)
{
PATH = “/Show”;
accessLog();
if (! flag == 1)
{
request->send_P(200, PSTR(“text/html”), HTML6, processor6);
}
end();
});
“/Show” request only works after “/SdBrowse” request has processed the selected url thru the above posted “notFound” function; when this happens, “/Show” request displays header, text file with scroll bar, and hyperlink to return to “/SdBrowse” list of “LOG” files..
if “/Show” request is made before “/SdBrowse” request; only header and hyperlink are displayed, no text file is displayed.
Trying to show web page with header, text file, and hyperlink; on selection of the filename in the “SdBrowser” request.
Best Regards,
William
Short under 3 minute, video of what I have completed so far…
Video: adding hyperlink to text.txt file.
RainGauge project link…
William
Hi William.
Thanks for sharing the resources to your project.
It seems that the processor() function for that HTML6 is not working at the right time, or it doesn’t have enough time to send all the data to be replaced in the processor() function.
Can you show me the processor6() function? Does it seem the problem can be there?
Regards,
Sara
Hi Sara,
Do I need to pass the uncfn variable to:
“serverAsync.on(“/Show”, HTTP_GET, [](AsyncWebServerRequest * request)”? Acts like it is not getting the filename.
HTML6 processor 6:
Start your code here
String processor6(const String& var)
{
//index6.h
if (var == F(“LINK”))
return linkAddress;
if (var == F(“UNCFN”))
return uncfn;
return String();
}
William
Hi again.
In your HTML6 file, can you remove the following the width=”100%”:
<br> <div><object data=http://%LINK%/%UNCFN% width=”100%” height=”700″></object></div>
So, it looks as follows:
<br> <div><object data=http://%LINK%/%UNCFN% height=”700″></object></div>
Let me know if that solves the issue.
Regards,
Sara
Good Morning,
Edited following line:
"<br> <div><object data=http://%LINK%/%UNCFN% height=”700″></object></div>"
Only change was a smaller text “window” on the left.
Made other changes and have confirmed filename is being passed. The notFound request is expecting a response; client side “somehow” displays a “text/plain”
text screen. If a “/path/Show” request is made; HTML6, processor6 display Web page with header, embedded text file, and hyperlink.
“/Show” request changes:
Commented out “/Show” request.
notFound function changes:
String notFound(AsyncWebServerRequest *request)
{
if (! request->url().endsWith(F(“.TXT”)))
{
request->send(404);
}
else
{
if (request->url().endsWith(F(“.TXT”)))
{
//.endsWith(F(“.txt”)))
// here comes some mambo-jambo to extract the filename from request->url()
int fnsstart = request->url().lastIndexOf(‘/’);
fn = request->url().substring(fnsstart);
uncfn = fn.substring(1);
}
}
request->send_P(200, PSTR(“text/html”), HTML6, processor6);
}
HTML6:
//index6.h
const char HTML6[] PROGMEM = R”====(
<!DOCTYPE HTML>
<html>
<head>
<title>Show</title>
</head>
<body>
<h2>Collected Observations
<br>
<br> <div><object data=http://%LINK%/%UNCFN% type=”text\html” width=”100%” height=”700″></object></div>
<br>
<br>
<a href=http://%LINK%/SdBrowse >SdBrowse</a></h2><br>
</body>
</html>
)====”;
processor6:
String processor6(const String& var)
{
//index6.h
if (var == F(“LINK”))
return linkAddress;
if (var == F(“UNCFN”))
return uncfn;
return String();
}
William
Hi again.
The reason why I told you to remove that line is because % signs other than the placeholders % signs might cause issues with the processor function. That’s why I thought that could be causing the issue.
Is it working with your new changes?
Regards,
Sara
Good Morning Sara,
Still not working as designed; have to display log file twice to be able to display HTML6, web page. First display is plain text; addtess in “Chrome” browser, “http://externalip/filename.” Second log text file has both “Header” and “Hyperlink’ using “text/html.” Displaying twice is the same log file; address in “Chrome” is “http://externalip/Show”” why I moved sending, the response for “HTML6, processor6” line to the “notFound” function. I have no SPIFFS file statements and no SPIFFS “request->send”; yet “Chrome” displays log file in “text/plain” text.
I am able to see correct “text” box area with width=”100%”; without the width=100%,” “text” box area where log file is displayed is much smaller. Thank you for suggest trying to remove width=100%.
William
How do you use “Espasyncwebserver,” request->send_P(200, PSTR(“text/html”), HTML, processor) with a HTML object data attribute, calling “LOG.TXT” from spiffs?
Sara, thanks for suggestion of removing “width=100%”; this and a bad url path were keeping text from being displayed, Today’s code (07/07/2021) still does not go from file name selection to display of the LOG file with a HTML Header, text box with scroll bar, and a Hyperlink at the bottom of the page to return to list of files. So, the same LOG file is display twice; once with plain text, and onec as Web page.
Viewing the HTML Log file on the project website requires selecting “File Browser” first; then use the back browser arrow and select “Show Log File.”
William
Hi.
The best way to do that is using JavaScript.
I’ve created a simple example. I hope you can understand it and use the concepts in your project.
This example:
- Shows a main web page with a button “Show”. When you click the button it makes a request on the /show URL.
- The ESP32 receives that request and sends the HTML to display the web page on the /show URL.
- The SHOW web page displays some HTML from the index_html_2 file. This file calls a file from spiffs with data called data.txt. –> So, this page calls a file from SPIFFS as you asked. The easiest way to do this is using JavaScript.
Here’s how it works:
When the SHOW page loads, a JavaScript function called getFile() runs. The getFile() function makes a request on the /get-file URL.
function getFile(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { console.log(this.responseText); document.getElementById("file-content").innerHTML=this.responseText; } }; xhr.open("GET", "/get-file", true); xhr.send(); }
When the ESP32 receives that request, it sends the corresponding file that is saved in SPIFFS (data.txt file);
server.on("/get-file", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(SPIFFS, "/data.txt", "text/txt"); });
The web page, receives the content of that file and places it on the HTML page on the paragraph with the “file-content” id:
if (this.readyState == 4 && this.status == 200) { console.log(this.responseText); document.getElementById("file-content").innerHTML=this.responseText; }
You can see the complete example here: https://gist.github.com/sarasantos/e3bef253c34109f7f76fb6e0be38f862
I hope this helps.
Regards,
Sara
Javascript is working as written, Thank you Sara! One remaining issue relating to text all “running” together.
HTML6, document that adds Header, text file, and Hyperlink returning to list of url filename requests.
HTML6:
Start your code here
//index6.h
const char HTML6[] PROGMEM = R”====(
<!DOCTYPE HTML>
<html>
<head>
<title>Show</title>
<style>
.main
{
type: text/csv;
font-size:14px;
width: auto;
height: auto;
margin: 30px;
}
</style>
</head>
<body>
<h2>Observations
<!– <a href=”/Show”><button>Show Log File</button></a> –>
<br>
<br>
<div class=”main” id=file-content></div>
<br>
<a href=http://%LINK%/SdBrowse >SdBrowse</a></h2><br>
</body>
<script> //Sara’s JavaScript example
window.addEventListener(‘load’, getFile);
function getFile(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.log(this.responseText);
document.getElementById(“file-content”).innerHTML=this.responseText;
}
};
xhr.open(“GET”, “/get-file”, true);
xhr.send();
}
</script>
</html>
)====”;
Processor2:
Start your code here
String processor2(const String& var) //creates list of url hyperlinks of filename in root directory
{ //processed by the notFound function.
//index2.h
String str;
File root = SPIFFS.open(“/”);
root.rewindDirectory(); //added 6/17/2021
File file = root.openNextFile();
//file.seek(0, SeekSet);
while (file)
{
if (strncmp(file.name(), “/LOG”, 4) == 0)
{
str += “<a href=\””;
str += file.name();
str += “\”>”;
str += file.name();
str += “</a>”;
str += ” “;
str += file.size();
str += “<br>\r\n”;
}
file = root.openNextFile();
}
root.close();
root.rewindDirectory();
if (var == F(“URLLINK”))
return str;
if (var == F(“LINK”))
return linkAddress;
if (var == F(“FILENAME”))
return file.name();
return String();
}
—————–
“GET” requests “/Show”:
Start your code here
serverAsync.on(“/Show”, HTTP_GET, [](AsyncWebServerRequest * request)
{
PATH = “/Show”;
accessLog();
if (! flag == 1)
{
request->send_P(200, PSTR(“text/html”), HTML6, processor6);
}
end();
});
——-
“GET” request “/get-file”:
Start your code here
serverAsync.on(“/get-file”, HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, fn, “text/html”);
});
————-
“notFound” function
Start your code here
String notFound(AsyncWebServerRequest *request) //Code from a “Github” formum member.
{
digitalWrite(online, HIGH); //turn-on online LED indicator
if (! request->url().endsWith(F(“.TXT”)))
{
request->send(404);
}
else
{
if (request->url().endsWith(F(“.TXT”)))
{
//.endsWith(F(“.txt”)))
// here comes some mambo-jambo to extract the filename from request->url()
int fnsstart = request->url().lastIndexOf(‘/’);
fn = request->url().substring(fnsstart);
uncfn = fn.substring(1);
urlPath = linkAddress + “/” + uncfn;
}
}
request->redirect(“/redirect/internal”);
digitalWrite(online, LOW); //turn-off online LED indicator
return fn;
}
—————
“GET” request for redirect:
Start your code here
serverAsync.on(“/redirect/internal”, HTTP_GET, [](AsyncWebServerRequest *request){
request->redirect(“/Show”); //recieced HTML status 302 code
});
William
Sara,
Could you implement this with your JavaScript example, please?
File API – FileReader (Text) –Fiddle meta
Looks promising:
File API – FileReader (Text) –image –“LOG07112021.TXT”
I do not have the skill set to implement. Will have to learn to write JavaScript…
at 73 years old, might take awhile.
Best Regards,
William
Sara found a better solution:
To solve that issue you can add the following styling to the paragraph that displays the data:
white-space: pre-wrap;
So, instead of this,
<p style width=”1600″; height=”700″; id=”file-content”><p> <======Note this styling did not help much.
You would have something like this:
<p style=”white-space: pre-wrap;”id=”file-content”></p>
I found this suggestion here: https://stackoverflow.com/questions/40417527/how-do-i-preserve-line-breaks-when-getting-text-from-a-textarea/40426477
Now; log files have header, text area and hyperlink!
William