mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Merge pull request #3878 from TD-er/bugfix/serve_files_memory_reduction
[Cleanup] Use less resources serving files from FS
This commit is contained in:
@@ -11,8 +11,11 @@
|
||||
#include "../Helpers/ESPEasy_time_calc.h"
|
||||
|
||||
|
||||
|
||||
#define CHUNKED_BUFFER_SIZE 400
|
||||
#ifdef ESP8266
|
||||
#define CHUNKED_BUFFER_SIZE 512
|
||||
#else
|
||||
#define CHUNKED_BUFFER_SIZE 1360
|
||||
#endif
|
||||
|
||||
Web_StreamingBuffer::Web_StreamingBuffer(void) : lowMemorySkip(false),
|
||||
initialRam(0), beforeTXRam(0), duringTXRam(0), finalRam(0), maxCoreUsage(0),
|
||||
|
||||
@@ -1366,56 +1366,60 @@ size_t SpiffsUsedBytes() {
|
||||
}
|
||||
|
||||
size_t SpiffsTotalBytes() {
|
||||
size_t result = 1; // Do not output 0, this may be used in divisions.
|
||||
|
||||
#ifdef ESP32
|
||||
result = ESPEASY_FS.totalBytes();
|
||||
#endif // ifdef ESP32
|
||||
#ifdef ESP8266
|
||||
fs::FSInfo fs_info;
|
||||
ESPEASY_FS.info(fs_info);
|
||||
result = fs_info.totalBytes;
|
||||
#endif // ifdef ESP8266
|
||||
static size_t result = 1; // Do not output 0, this may be used in divisions.
|
||||
if (result == 1) {
|
||||
#ifdef ESP32
|
||||
result = ESPEASY_FS.totalBytes();
|
||||
#endif // ifdef ESP32
|
||||
#ifdef ESP8266
|
||||
fs::FSInfo fs_info;
|
||||
ESPEASY_FS.info(fs_info);
|
||||
result = fs_info.totalBytes;
|
||||
#endif // ifdef ESP8266
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t SpiffsBlocksize() {
|
||||
size_t result = 8192; // Some default viable for most 1 MB file systems
|
||||
|
||||
#ifdef ESP32
|
||||
result = 8192; // Just assume 8k, since we cannot query it
|
||||
#endif // ifdef ESP32
|
||||
#ifdef ESP8266
|
||||
fs::FSInfo fs_info;
|
||||
ESPEASY_FS.info(fs_info);
|
||||
result = fs_info.blockSize;
|
||||
#endif // ifdef ESP8266
|
||||
static size_t result = 1;
|
||||
if (result == 1) {
|
||||
#ifdef ESP32
|
||||
result = 8192; // Just assume 8k, since we cannot query it
|
||||
#endif // ifdef ESP32
|
||||
#ifdef ESP8266
|
||||
fs::FSInfo fs_info;
|
||||
ESPEASY_FS.info(fs_info);
|
||||
result = fs_info.blockSize;
|
||||
#endif // ifdef ESP8266
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t SpiffsPagesize() {
|
||||
size_t result = 256; // Most common
|
||||
|
||||
#ifdef ESP32
|
||||
result = 256; // Just assume 256, since we cannot query it
|
||||
#endif // ifdef ESP32
|
||||
#ifdef ESP8266
|
||||
fs::FSInfo fs_info;
|
||||
ESPEASY_FS.info(fs_info);
|
||||
result = fs_info.pageSize;
|
||||
#endif // ifdef ESP8266
|
||||
static size_t result = 1;
|
||||
if (result == 1) {
|
||||
#ifdef ESP32
|
||||
result = 256; // Just assume 256, since we cannot query it
|
||||
#endif // ifdef ESP32
|
||||
#ifdef ESP8266
|
||||
fs::FSInfo fs_info;
|
||||
ESPEASY_FS.info(fs_info);
|
||||
result = fs_info.pageSize;
|
||||
#endif // ifdef ESP8266
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t SpiffsFreeSpace() {
|
||||
int freeSpace = SpiffsTotalBytes() - SpiffsUsedBytes();
|
||||
const size_t blocksize = SpiffsBlocksize();
|
||||
|
||||
if (freeSpace < static_cast<int>(2 * SpiffsBlocksize())) {
|
||||
if (freeSpace < static_cast<int>(2 * blocksize)) {
|
||||
// Not enough free space left to store anything
|
||||
// There needs to be minimum of 2 free blocks.
|
||||
return 0;
|
||||
}
|
||||
return freeSpace - 2 * SpiffsBlocksize();
|
||||
return freeSpace - 2 * blocksize;
|
||||
}
|
||||
|
||||
bool SpiffsFull() {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifdef ESP32
|
||||
#include <soc/soc.h>
|
||||
#include <soc/efuse_reg.h>
|
||||
#include <rom/spi_flash.h>
|
||||
#endif
|
||||
|
||||
/********************************************************************************************\
|
||||
@@ -480,21 +481,30 @@ int espeasy_analogRead(int pin, bool readAsTouch) {
|
||||
Hardware information
|
||||
\*********************************************************************************************/
|
||||
uint32_t getFlashChipId() {
|
||||
uint32_t flashChipId = 0;
|
||||
// Cache since size does not change
|
||||
static uint32_t flashChipId = 0;
|
||||
if (flashChipId == 0) {
|
||||
#ifdef ESP32
|
||||
//esp_flash_read_id(nullptr, &flashChipId);
|
||||
flashChipId = g_rom_flashchip.device_id;
|
||||
// esp_flash_read_id(nullptr, &flashChipId);
|
||||
#elif defined(ESP8266)
|
||||
flashChipId = ESP.getFlashChipId();
|
||||
flashChipId = ESP.getFlashChipId();
|
||||
#endif
|
||||
}
|
||||
return flashChipId;
|
||||
}
|
||||
|
||||
uint32_t getFlashRealSizeInBytes() {
|
||||
#if defined(ESP32)
|
||||
return ESP.getFlashChipSize();
|
||||
#else // if defined(ESP32)
|
||||
return ESP.getFlashChipRealSize(); // ESP.getFlashChipSize();
|
||||
#endif // if defined(ESP32)
|
||||
// Cache since size does not change
|
||||
static uint32_t res = 0;
|
||||
if (res == 0) {
|
||||
#if defined(ESP32)
|
||||
res = ESP.getFlashChipSize();
|
||||
#else // if defined(ESP32)
|
||||
res = ESP.getFlashChipRealSize(); // ESP.getFlashChipSize();
|
||||
#endif // if defined(ESP32)
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -519,17 +529,17 @@ uint8_t getFlashChipVendorId() {
|
||||
return ESP.getFlashChipVendorId();
|
||||
#else // ifdef PUYA_SUPPORT
|
||||
# if defined(ESP8266)
|
||||
uint32_t flashChipId = ESP.getFlashChipId();
|
||||
// Cache since size does not change
|
||||
static uint32_t flashChipId = ESP.getFlashChipId();
|
||||
return flashChipId & 0x000000ff;
|
||||
# elif defined(ESP32)
|
||||
|
||||
return 0xFF; // Not an existing function for ESP32
|
||||
# endif // if defined(ESP8266)
|
||||
#endif // ifdef PUYA_SUPPORT
|
||||
return 0xFF; // Not an existing function for ESP32
|
||||
}
|
||||
|
||||
bool flashChipVendorPuya() {
|
||||
uint8_t vendorId = getFlashChipVendorId();
|
||||
const uint8_t vendorId = getFlashChipVendorId();
|
||||
|
||||
return vendorId == 0x85; // 0x146085 PUYA
|
||||
}
|
||||
@@ -550,13 +560,17 @@ uint32_t getChipId() {
|
||||
}
|
||||
|
||||
uint8_t getChipCores() {
|
||||
uint8_t cores = 1;
|
||||
#ifdef ESP32
|
||||
#ifdef ESP8266
|
||||
return 1;
|
||||
#else
|
||||
static uint8_t cores = 0;
|
||||
if (cores == 0) {
|
||||
esp_chip_info_t chip_info;
|
||||
esp_chip_info(&chip_info);
|
||||
cores = chip_info.cores;
|
||||
#endif
|
||||
}
|
||||
return cores;
|
||||
#endif
|
||||
}
|
||||
|
||||
const __FlashStringHelper * getChipModel() {
|
||||
@@ -599,6 +613,19 @@ uint8_t getChipRevision() {
|
||||
return rev;
|
||||
}
|
||||
|
||||
uint32_t getSketchSize() {
|
||||
// Cache the value as this never changes during run time.
|
||||
static uint32_t res = ESP.getSketchSize();
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t getFreeSketchSpace() {
|
||||
// Cache the value as this never changes during run time.
|
||||
static uint32_t res = ESP.getFreeSketchSpace();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ESP8266
|
||||
void readBootCause() {
|
||||
lastBootCause = BOOT_CAUSE_MANUAL_REBOOT;
|
||||
|
||||
@@ -82,6 +82,10 @@ const __FlashStringHelper * getChipModel();
|
||||
|
||||
uint8_t getChipRevision();
|
||||
|
||||
uint32_t getSketchSize();
|
||||
|
||||
uint32_t getFreeSketchSpace();
|
||||
|
||||
/********************************************************************************************\
|
||||
Boot information
|
||||
\*********************************************************************************************/
|
||||
|
||||
@@ -12,8 +12,8 @@ bool OTA_possible(uint32_t& maxSketchSize, bool& use2step) {
|
||||
|
||||
// Compute the current free space and sketch size, rounded to 4k blocks.
|
||||
// These block bounaries are needed for erasing a full block on flash.
|
||||
const uint32_t freeSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
const uint32_t currentSketchSize = (ESP.getSketchSize() + 0x1000) & 0xFFFFF000;
|
||||
const uint32_t freeSketchSpace = (getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
const uint32_t currentSketchSize = (getSketchSize() + 0x1000) & 0xFFFFF000;
|
||||
const uint32_t smallestOtaImageSizeNeeded = (((SMALLEST_OTA_IMAGE + 16) + 0x1000) & 0xFFFFF000);
|
||||
const bool otaPossible = freeSketchSpace >= smallestOtaImageSizeNeeded;
|
||||
use2step = freeSketchSpace < currentSketchSize; // Assume the new image has the same size.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../Globals/Cache.h"
|
||||
#include "../Helpers/ESPEasy_Storage.h"
|
||||
#include "../WebServer/HTML_wrappers.h"
|
||||
#include "../WebServer/LoadFromFS.h"
|
||||
|
||||
String generate_external_URL(const String& fname) {
|
||||
String url;
|
||||
@@ -14,27 +15,28 @@ String generate_external_URL(const String& fname) {
|
||||
|
||||
|
||||
void serve_CSS() {
|
||||
String url = F("esp.css");
|
||||
if (!fileExists(url))
|
||||
const String cssFile = F("esp.css");
|
||||
if (fileExists(cssFile))
|
||||
{
|
||||
#ifndef WEBSERVER_CSS
|
||||
url = generate_external_URL(F("espeasy_default.css"));
|
||||
#else
|
||||
addHtml(F("<style>"));
|
||||
|
||||
// Send CSS in chunks
|
||||
TXBuffer.addFlashString((PGM_P)FPSTR(DATA_ESPEASY_DEFAULT_MIN_CSS));
|
||||
streamFromFS(cssFile);
|
||||
addHtml(F("</style>"));
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef WEBSERVER_CSS
|
||||
addHtml(F("<link"));
|
||||
addHtmlAttribute(F("rel"), F("stylesheet"));
|
||||
addHtmlAttribute(F("type"), F("text/css"));
|
||||
addHtmlAttribute(F("href"), url);
|
||||
addHtmlAttribute(F("href"), generate_external_URL(F("espeasy_default.css")));
|
||||
addHtml('/');
|
||||
addHtml('>');
|
||||
#else
|
||||
addHtml(F("<style>"));
|
||||
|
||||
// Send CSS in chunks
|
||||
TXBuffer.addFlashString((PGM_P)FPSTR(DATA_ESPEASY_DEFAULT_MIN_CSS));
|
||||
addHtml(F("</style>"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void serve_favicon() {
|
||||
@@ -112,10 +114,15 @@ void serve_JS(JSfiles_e JSfile) {
|
||||
html_add_script_end();
|
||||
return;
|
||||
#endif
|
||||
addHtml(F("<script"));
|
||||
addHtml(F(" defer"));
|
||||
addHtmlAttribute(F("src"), url);
|
||||
addHtml('>');
|
||||
html_add_script_end();
|
||||
return;
|
||||
}
|
||||
addHtml(F("<script"));
|
||||
addHtml(F(" defer"));
|
||||
addHtmlAttribute(F("src"), url);
|
||||
addHtml('>');
|
||||
// Now stream the file directly from the file system.
|
||||
html_add_script(false);
|
||||
streamFromFS(url);
|
||||
html_add_script_end();
|
||||
}
|
||||
@@ -32,9 +32,7 @@ void handleNotFound() {
|
||||
if (handle_rules_edit(web_server.uri())) { return; }
|
||||
#endif
|
||||
|
||||
if (loadFromFS(true, web_server.uri())) { return; }
|
||||
|
||||
if (loadFromFS(false, web_server.uri())) { return; }
|
||||
if (loadFromFS(web_server.uri())) { return; }
|
||||
String message = F("URI: ");
|
||||
message += web_server.uri();
|
||||
message += F("\nMethod: ");
|
||||
|
||||
@@ -23,27 +23,16 @@
|
||||
// ********************************************************************************
|
||||
// Web Interface custom page handler
|
||||
// ********************************************************************************
|
||||
boolean handle_custom(String path) {
|
||||
// path is a deepcopy, since it will be changed.
|
||||
bool handle_custom(const String& path) {
|
||||
#ifndef BUILD_NO_RAM_TRACKER
|
||||
checkRAM(F("handle_custom"));
|
||||
#endif
|
||||
|
||||
if (!clientIPallowed()) { return false; }
|
||||
|
||||
#ifdef ESP8266
|
||||
// For ESP32 remove the leading slash
|
||||
path = path.substring(1);
|
||||
#endif
|
||||
|
||||
// create a dynamic custom page, parsing task values into [<taskname>#<taskvalue>] placeholders and parsing %xx% system variables
|
||||
fs::File dataFile = tryOpenFile(path.c_str(), "r");
|
||||
#ifdef ESP8266
|
||||
const bool dashboardPage = path.startsWith(F("dashboard"));
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
const bool dashboardPage = path.startsWith(F("/dashboard"));
|
||||
#endif
|
||||
const bool dashboardPage = path.startsWith(F("dashboard")) || path.startsWith(F("/dashboard"));
|
||||
|
||||
if (!dataFile && !dashboardPage) {
|
||||
return false; // unknown file that does not exist...
|
||||
@@ -147,14 +136,35 @@ boolean handle_custom(String path) {
|
||||
|
||||
if (dataFile)
|
||||
{
|
||||
String page;
|
||||
page.reserve(dataFile.size());
|
||||
|
||||
while (dataFile.available()) {
|
||||
page += ((char)dataFile.read());
|
||||
// Read the file per line and serve per line to reduce amount of memory needed.
|
||||
int available = dataFile.available();
|
||||
String line;
|
||||
line.reserve(128);
|
||||
while (available > 0) {
|
||||
uint32_t chunksize = 64;
|
||||
if (available < chunksize) {
|
||||
chunksize = available;
|
||||
}
|
||||
uint8_t buf[64] = {0};
|
||||
const size_t read = dataFile.read(buf, chunksize);
|
||||
if (read == chunksize) {
|
||||
for (uint32_t i = 0; i < chunksize; ++i) {
|
||||
const char c = (char)buf[i];
|
||||
line += c;
|
||||
if (c == '\n') {
|
||||
addHtml(parseTemplate(line));
|
||||
line.clear();
|
||||
line.reserve(128);
|
||||
}
|
||||
}
|
||||
available = dataFile.available();
|
||||
} else {
|
||||
available = 0;
|
||||
}
|
||||
}
|
||||
if (!line.isEmpty()) {
|
||||
addHtml(parseTemplate(line));
|
||||
}
|
||||
|
||||
addHtml(parseTemplate(page));
|
||||
dataFile.close();
|
||||
}
|
||||
else // if the requestef file does not exist, create a default action in case the page is named "dashboard*"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// ********************************************************************************
|
||||
// Web Interface custom page handler
|
||||
// ********************************************************************************
|
||||
boolean handle_custom(String path);
|
||||
bool handle_custom(const String& path);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,46 +1,66 @@
|
||||
#include "../WebServer/LoadFromFS.h"
|
||||
|
||||
#include "../WebServer/WebServer.h"
|
||||
#include "../WebServer/CustomPage.h"
|
||||
#include "../Globals/RamTracker.h"
|
||||
|
||||
#include "../Helpers/ESPEasy_Storage.h"
|
||||
#include "../Helpers/Network.h"
|
||||
|
||||
#ifdef FEATURE_SD
|
||||
#include <SD.h>
|
||||
#endif
|
||||
#include "../WebServer/CustomPage.h"
|
||||
#include "../WebServer/HTML_wrappers.h"
|
||||
#include "../WebServer/WebServer.h"
|
||||
|
||||
bool match_ext(const String& path, const __FlashStringHelper * ext) {
|
||||
return (path.endsWith(ext) || path.endsWith(String(ext) + F(".gz")));
|
||||
#ifdef FEATURE_SD
|
||||
# include <SD.h>
|
||||
#endif // ifdef FEATURE_SD
|
||||
|
||||
bool match_ext(const String& path, const __FlashStringHelper *ext) {
|
||||
return path.endsWith(ext) || path.endsWith(String(ext) + F(".gz"));
|
||||
}
|
||||
|
||||
bool gzipEncoded(const String& path) {
|
||||
return path.endsWith(F(".gz"));
|
||||
}
|
||||
|
||||
// ********************************************************************************
|
||||
// Web Interface server web file from FS
|
||||
// ********************************************************************************
|
||||
bool loadFromFS(boolean spiffs, String path) {
|
||||
// path is a deepcopy, since it will be changed here.
|
||||
#ifndef BUILD_NO_RAM_TRACKER
|
||||
checkRAM(F("loadFromFS"));
|
||||
#endif
|
||||
|
||||
statusLED(true);
|
||||
|
||||
String dataType = F("text/plain");
|
||||
bool mustCheckCredentials = false;
|
||||
|
||||
String fileFromUrl(String path) {
|
||||
const int questionmarkPos = path.indexOf('?');
|
||||
|
||||
if (questionmarkPos >= 0) {
|
||||
path = path.substring(0, questionmarkPos);
|
||||
}
|
||||
|
||||
// First prepend slash
|
||||
if (!path.startsWith(F("/"))) {
|
||||
path = String(F("/")) + path;
|
||||
}
|
||||
|
||||
|
||||
if (path.endsWith(F("/"))) { path += F("index.htm"); }
|
||||
|
||||
#ifdef ESP8266
|
||||
// Remove leading slash to generate filename from it.
|
||||
if (path.startsWith(F("/"))) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// ********************************************************************************
|
||||
// Web Interface server web file from FS
|
||||
// ********************************************************************************
|
||||
bool loadFromFS(String path) {
|
||||
// path is a deepcopy, since it will be changed here.
|
||||
#ifndef BUILD_NO_RAM_TRACKER
|
||||
checkRAM(F("loadFromFS"));
|
||||
#endif // ifndef BUILD_NO_RAM_TRACKER
|
||||
|
||||
statusLED(true);
|
||||
|
||||
const __FlashStringHelper* dataType = F("text/plain");
|
||||
bool mustCheckCredentials = false;
|
||||
|
||||
path = fileFromUrl(path);
|
||||
|
||||
if (path.endsWith(F(".src"))) { path = path.substring(0, path.lastIndexOf(".")); }
|
||||
else if (match_ext(path, F(".htm")) || match_ext(path, F(".html"))) { dataType = F("text/html"); }
|
||||
else if (match_ext(path, F(".css"))) { dataType = F("text/css"); }
|
||||
@@ -52,15 +72,15 @@ bool loadFromFS(boolean spiffs, String path) {
|
||||
else if (path.endsWith(F(".svg"))) { dataType = F("image/svg+xml"); }
|
||||
else if (path.endsWith(F(".json"))) { dataType = F("application/json"); }
|
||||
else if (path.endsWith(F(".txt")) ||
|
||||
path.endsWith(F(".dat"))) {
|
||||
path.endsWith(F(".dat"))) {
|
||||
mustCheckCredentials = true;
|
||||
dataType = F("application/octet-stream");
|
||||
dataType = F("application/octet-stream");
|
||||
}
|
||||
#ifdef WEBSERVER_CUSTOM
|
||||
else if (path.endsWith(F(".esp"))) {
|
||||
return handle_custom(path);
|
||||
return handle_custom(path);
|
||||
}
|
||||
#endif
|
||||
#endif // ifdef WEBSERVER_CUSTOM
|
||||
else {
|
||||
mustCheckCredentials = true;
|
||||
}
|
||||
@@ -78,53 +98,86 @@ bool loadFromFS(boolean spiffs, String path) {
|
||||
}
|
||||
#endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
#if !defined(ESP32)
|
||||
path = path.substring(1);
|
||||
#endif // if !defined(ESP32)
|
||||
fs::File f;
|
||||
|
||||
if (spiffs)
|
||||
{
|
||||
if (!fileExists(path)) {
|
||||
return false;
|
||||
}
|
||||
fs::File dataFile = tryOpenFile(path.c_str(), "r");
|
||||
|
||||
if (!dataFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// prevent reloading stuff on every click
|
||||
web_server.sendHeader(F("Cache-Control"), F("max-age=3600, public"));
|
||||
web_server.sendHeader(F("Vary"), "*");
|
||||
web_server.sendHeader(F("ETag"), F("\"2.0.0\""));
|
||||
|
||||
if (path.endsWith(F(".dat"))) {
|
||||
web_server.sendHeader(F("Content-Disposition"), F("attachment;"));
|
||||
}
|
||||
|
||||
web_server.streamFile(dataFile, dataType);
|
||||
dataFile.close();
|
||||
// Search flash file system first, then SD if present
|
||||
f = tryOpenFile(path.c_str(), "r");
|
||||
#ifdef FEATURE_SD
|
||||
if (!f) {
|
||||
f = SD.open(path.c_str(), "r");
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef FEATURE_SD
|
||||
File dataFile = SD.open(path.c_str());
|
||||
#endif // ifdef FEATURE_SD
|
||||
|
||||
if (!dataFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path.endsWith(F(".DAT"))) {
|
||||
web_server.sendHeader(F("Content-Disposition"), F("attachment;"));
|
||||
}
|
||||
web_server.streamFile(dataFile, dataType);
|
||||
dataFile.close();
|
||||
#else // ifdef FEATURE_SD
|
||||
|
||||
// File from SD requested, but no SD support.
|
||||
if (!f) {
|
||||
return false;
|
||||
#endif // ifdef FEATURE_SD
|
||||
}
|
||||
|
||||
// prevent reloading stuff on every click
|
||||
web_server.sendHeader(F("Cache-Control"), F("max-age=3600, public"));
|
||||
web_server.sendHeader(F("Vary"), "*");
|
||||
web_server.sendHeader(F("ETag"), F("\"2.0.0\""));
|
||||
|
||||
if (path.endsWith(F(".dat"))) {
|
||||
web_server.sendHeader(F("Content-Disposition"), F("attachment;"));
|
||||
}
|
||||
if (gzipEncoded(path)) {
|
||||
web_server.sendHeader(F("Content-Encoding"), F("gzip"));
|
||||
}
|
||||
|
||||
web_server.streamFile(f, dataType);
|
||||
f.close();
|
||||
|
||||
statusLED(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t streamFromFS(String path, bool htmlEscape) {
|
||||
// path is a deepcopy, since it will be changed here.
|
||||
path = fileFromUrl(path);
|
||||
statusLED(true);
|
||||
|
||||
size_t bytesStreamed = 0;
|
||||
|
||||
fs::File f;
|
||||
|
||||
// Search flash file system first, then SD if present
|
||||
f = tryOpenFile(path.c_str(), "r");
|
||||
#ifdef FEATURE_SD
|
||||
if (!f) {
|
||||
f = SD.open(path.c_str(), "r");
|
||||
}
|
||||
#endif // ifdef FEATURE_SD
|
||||
|
||||
if (!f) {
|
||||
return bytesStreamed;
|
||||
}
|
||||
|
||||
int available = f.available();
|
||||
String escaped;
|
||||
while (available > 0) {
|
||||
uint32_t chunksize = 64;
|
||||
if (available < chunksize) {
|
||||
chunksize = available;
|
||||
}
|
||||
uint8_t buf[64] = {0};
|
||||
const size_t read = f.read(buf, chunksize);
|
||||
if (read == chunksize) {
|
||||
for (uint32_t i = 0; i < chunksize; ++i) {
|
||||
const char c = (char)buf[i];
|
||||
if (htmlEscape && htmlEscapeChar(c, escaped)) {
|
||||
addHtml(escaped);
|
||||
} else {
|
||||
addHtml(c);
|
||||
}
|
||||
}
|
||||
bytesStreamed += read;
|
||||
available = f.available();
|
||||
} else {
|
||||
available = 0;
|
||||
}
|
||||
}
|
||||
statusLED(true);
|
||||
|
||||
f.close();
|
||||
return bytesStreamed;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
#include "../WebServer/common.h"
|
||||
|
||||
|
||||
bool loadFromFS(boolean spiffs, String path);
|
||||
bool loadFromFS(String path);
|
||||
|
||||
|
||||
// Send the content of a file directly to the webserver, like addHtml()
|
||||
// Return is nr bytes streamed.
|
||||
size_t streamFromFS(String path, bool htmlEscape = false);
|
||||
|
||||
#endif
|
||||
@@ -81,15 +81,8 @@ void handle_root() {
|
||||
navMenuIndex = 0;
|
||||
|
||||
// if index.htm exists on FS serve that one (first check if gziped version exists)
|
||||
if (loadFromFS(true, F("/index.htm.gz"))) { return; }
|
||||
#ifdef FEATURE_SD
|
||||
if (loadFromFS(false, F("/index.htm.gz"))) { return; }
|
||||
#endif
|
||||
|
||||
if (loadFromFS(true, F("/index.htm"))) { return; }
|
||||
#ifdef FEATURE_SD
|
||||
if (loadFromFS(false, F("/index.htm"))) { return; }
|
||||
#endif
|
||||
if (loadFromFS(F("/index.htm.gz"))) { return; }
|
||||
if (loadFromFS(F("/index.htm"))) { return; }
|
||||
|
||||
TXBuffer.startStream();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../WebServer/WebServer.h"
|
||||
#include "../WebServer/AccessControl.h"
|
||||
#include "../WebServer/HTML_wrappers.h"
|
||||
#include "../WebServer/LoadFromFS.h"
|
||||
#include "../WebServer/Markup.h"
|
||||
#include "../WebServer/Markup_Buttons.h"
|
||||
#include "../WebServer/Markup_Forms.h"
|
||||
@@ -557,7 +558,7 @@ void Rule_showRuleTextArea(const String& fileName) {
|
||||
size_t size = 0;
|
||||
|
||||
addHtml(F("<textarea id='rules' name='rules' rows='30' wrap='off'>"));
|
||||
size = streamFile_htmlEscape(fileName);
|
||||
size = streamFromFS(fileName, true);
|
||||
addHtml(F("</textarea>"));
|
||||
|
||||
html_TR_TD();
|
||||
|
||||
@@ -167,7 +167,7 @@ void handle_sysinfo_json() {
|
||||
json_open(false, F("storage"));
|
||||
|
||||
# if defined(ESP8266)
|
||||
uint32_t flashChipId = ESP.getFlashChipId();
|
||||
uint32_t flashChipId = getFlashChipId();
|
||||
|
||||
// Set to HEX may be something like 0x1640E0.
|
||||
// Where manufacturer is 0xE0 and device is 0x4016.
|
||||
@@ -205,8 +205,8 @@ void handle_sysinfo_json() {
|
||||
|
||||
json_number(F("writes"), String(RTC.flashDayCounter));
|
||||
json_number(F("flash_counter"), String(RTC.flashCounter));
|
||||
json_number(F("sketch_size"), String(ESP.getSketchSize() / 1024));
|
||||
json_number(F("sketch_free"), String(ESP.getFreeSketchSpace() / 1024));
|
||||
json_number(F("sketch_size"), String(getSketchSize() / 1024));
|
||||
json_number(F("sketch_free"), String(getFreeSketchSpace() / 1024));
|
||||
|
||||
json_number(F("spiffs_size"), String(SpiffsTotalBytes() / 1024));
|
||||
json_number(F("spiffs_free"), String(SpiffsFreeSpace() / 1024));
|
||||
@@ -612,8 +612,8 @@ void handle_sysinfo_Storage() {
|
||||
uint32_t flashDevice = (flashChipId & 0xFF00) | ((flashChipId >> 16) & 0xFF);
|
||||
addHtml(formatToHex(flashDevice));
|
||||
}
|
||||
uint32_t realSize = getFlashRealSizeInBytes();
|
||||
uint32_t ideSize = ESP.getFlashChipSize();
|
||||
const uint32_t realSize = getFlashRealSizeInBytes();
|
||||
const uint32_t ideSize = ESP.getFlashChipSize();
|
||||
|
||||
addRowLabel(LabelType::FLASH_CHIP_REAL_SIZE);
|
||||
addHtmlInt(realSize / 1024);
|
||||
@@ -632,17 +632,14 @@ void handle_sysinfo_Storage() {
|
||||
FlashMode_t ideMode = ESP.getFlashChipMode();
|
||||
addRowLabel(LabelType::FLASH_IDE_MODE);
|
||||
{
|
||||
String html;
|
||||
|
||||
switch (ideMode) {
|
||||
case FM_QIO: html += F("QIO"); break;
|
||||
case FM_QOUT: html += F("QOUT"); break;
|
||||
case FM_DIO: html += F("DIO"); break;
|
||||
case FM_DOUT: html += F("DOUT"); break;
|
||||
case FM_QIO: addHtml(F("QIO")); break;
|
||||
case FM_QOUT: addHtml(F("QOUT")); break;
|
||||
case FM_DIO: addHtml(F("DIO")); break;
|
||||
case FM_DOUT: addHtml(F("DOUT")); break;
|
||||
default:
|
||||
html += getUnknownString(); break;
|
||||
addHtml(getUnknownString()); break;
|
||||
}
|
||||
addHtml(html);
|
||||
}
|
||||
# endif // if defined(ESP8266)
|
||||
|
||||
@@ -663,9 +660,9 @@ void handle_sysinfo_Storage() {
|
||||
{
|
||||
String html;
|
||||
html.reserve(32);
|
||||
html += ESP.getSketchSize() / 1024;
|
||||
html += getSketchSize() / 1024;
|
||||
html += F(" kB (");
|
||||
html += ESP.getFreeSketchSpace() / 1024;
|
||||
html += getFreeSketchSpace() / 1024;
|
||||
html += F(" kB free)");
|
||||
addHtml(html);
|
||||
}
|
||||
|
||||
@@ -235,32 +235,6 @@ void sendHeadandTail_stdtemplate(boolean Tail, boolean rebooting) {
|
||||
}
|
||||
}
|
||||
|
||||
size_t streamFile_htmlEscape(const String& fileName)
|
||||
{
|
||||
fs::File f = tryOpenFile(fileName, "r");
|
||||
size_t size = 0;
|
||||
|
||||
if (f)
|
||||
{
|
||||
while (f.available())
|
||||
{
|
||||
char c = (char)f.read();
|
||||
|
||||
String escaped;
|
||||
|
||||
if (htmlEscapeChar(c, escaped)) {
|
||||
addHtml(escaped);
|
||||
} else {
|
||||
addHtml(c);
|
||||
}
|
||||
++size;
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
bool captivePortal() {
|
||||
const bool fromAP = web_server.client().localIP() == apIP;
|
||||
const bool hasWiFiCredentials = SecuritySettings.hasWiFiCredentials();
|
||||
@@ -1039,11 +1013,21 @@ String getControllerSymbol(uint8_t index)
|
||||
return ret;
|
||||
}
|
||||
*/
|
||||
void addSVG_param(const String& key, float value) {
|
||||
|
||||
void addSVG_param(const __FlashStringHelper * key, int value) {
|
||||
addHtml(' ');
|
||||
addHtml(key);
|
||||
addHtml('=');
|
||||
addHtml('\"');
|
||||
addHtmlInt(value);
|
||||
addHtml('\"');
|
||||
}
|
||||
|
||||
void addSVG_param(const __FlashStringHelper * key, float value) {
|
||||
addSVG_param(key, String(value, 2));
|
||||
}
|
||||
|
||||
void addSVG_param(const String& key, const String& value) {
|
||||
void addSVG_param(const __FlashStringHelper * key, const String& value) {
|
||||
addHtml(' ');
|
||||
addHtml(key);
|
||||
addHtml('=');
|
||||
@@ -1076,8 +1060,8 @@ void createSvgRect(const String& classname,
|
||||
addSVG_param(F("stroke"), formatToHex(strokeColor, F("#")));
|
||||
addSVG_param(F("stroke-width"), strokeWidth);
|
||||
}
|
||||
addSVG_param("x", xoffset);
|
||||
addSVG_param("y", yoffset);
|
||||
addSVG_param(F("x"), xoffset);
|
||||
addSVG_param(F("y"), yoffset);
|
||||
addSVG_param(F("width"), width);
|
||||
addSVG_param(F("height"), height);
|
||||
addSVG_param(F("rx"), rx);
|
||||
@@ -1121,10 +1105,6 @@ void createSvgTextElement(const String& text, float textXoffset, float textYoffs
|
||||
#define SVG_BAR_HEIGHT 16
|
||||
#define SVG_BAR_WIDTH 400
|
||||
|
||||
void write_SVG_image_header(int width, int height) {
|
||||
write_SVG_image_header(width, height, false);
|
||||
}
|
||||
|
||||
void write_SVG_image_header(int width, int height, bool useViewbox) {
|
||||
addHtml(F("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\""));
|
||||
addHtmlInt(width);
|
||||
@@ -1259,7 +1239,9 @@ void getStorageTableSVG(SettingsType::Enum settingsType) {
|
||||
float textYoffset = yOffset + 0.9f * SVG_BAR_HEIGHT;
|
||||
|
||||
if (struct_size != 0) {
|
||||
String text = formatHumanReadable(struct_size, 1024);
|
||||
String text;
|
||||
text.reserve(32);
|
||||
text = formatHumanReadable(struct_size, 1024);
|
||||
text += '/';
|
||||
text += formatHumanReadable(max_size, 1024);
|
||||
text += F(" per item");
|
||||
|
||||
@@ -40,7 +40,6 @@ void sendHeadandTail(const String& tmplName,
|
||||
void sendHeadandTail_stdtemplate(boolean Tail = false,
|
||||
boolean rebooting = false);
|
||||
|
||||
size_t streamFile_htmlEscape(const String& fileName);
|
||||
|
||||
void WebServerInit();
|
||||
|
||||
@@ -143,10 +142,13 @@ String getControllerSymbol(uint8_t index);
|
||||
/*
|
||||
String getValueSymbol(uint8_t index);
|
||||
*/
|
||||
void addSVG_param(const String& key,
|
||||
void addSVG_param(const __FlashStringHelper * key,
|
||||
int value);
|
||||
|
||||
void addSVG_param(const __FlashStringHelper * key,
|
||||
float value);
|
||||
|
||||
void addSVG_param(const String& key,
|
||||
void addSVG_param(const __FlashStringHelper * key,
|
||||
const String& value);
|
||||
|
||||
void createSvgRect_noStroke(const __FlashStringHelper * classname,
|
||||
@@ -181,12 +183,9 @@ void createSvgTextElement(const String& text,
|
||||
float textXoffset,
|
||||
float textYoffset);
|
||||
|
||||
void write_SVG_image_header(int width,
|
||||
int height);
|
||||
|
||||
void write_SVG_image_header(int width,
|
||||
int height,
|
||||
bool useViewbox);
|
||||
bool useViewbox = false);
|
||||
|
||||
/*
|
||||
void getESPeasyLogo(int width_pixels);
|
||||
|
||||
Reference in New Issue
Block a user