fixes webserver uploads and general stability
This commit is contained in:
parent
c171813045
commit
bab374a675
@ -52,6 +52,13 @@ void RecentBooksStore::clearAll() {
|
||||
Serial.printf("[%lu] [RBS] Cleared all recent books\n", millis());
|
||||
}
|
||||
|
||||
void RecentBooksStore::clearFromMemory() {
|
||||
const size_t count = recentBooks.size();
|
||||
recentBooks.clear();
|
||||
recentBooks.shrink_to_fit(); // Actually free the vector capacity
|
||||
Serial.printf("[%lu] [RBS] Cleared %d recent books from memory (not saved)\n", millis(), count);
|
||||
}
|
||||
|
||||
bool RecentBooksStore::saveToFile() const {
|
||||
// Make sure the directory exists
|
||||
SdMan.mkdir("/.crosspoint");
|
||||
|
||||
@ -29,9 +29,14 @@ class RecentBooksStore {
|
||||
// Returns true if the book was found and removed
|
||||
bool removeBook(const std::string& path);
|
||||
|
||||
// Clear all recent books from the list
|
||||
// Clear all recent books from the list (and save to file)
|
||||
void clearAll();
|
||||
|
||||
// Clear recent books from memory without saving to file
|
||||
// Used to free memory when entering modes that don't need this data (e.g., File Transfer)
|
||||
// Call loadFromFile() to restore the data when needed again
|
||||
void clearFromMemory();
|
||||
|
||||
// Get the list of recent books (most recent first)
|
||||
const std::vector<RecentBook>& getBooks() const { return recentBooks; }
|
||||
|
||||
|
||||
27
src/main.cpp
27
src/main.cpp
@ -381,6 +381,15 @@ void onGoToListsOrPinned() {
|
||||
|
||||
void onGoToFileTransfer() {
|
||||
exitActivity();
|
||||
|
||||
// Free memory not needed during file transfer to maximize heap for webserver
|
||||
RECENT_BOOKS.clearFromMemory();
|
||||
APP_STATE.openBookTitle.clear();
|
||||
APP_STATE.openBookTitle.shrink_to_fit();
|
||||
APP_STATE.openBookAuthor.clear();
|
||||
APP_STATE.openBookAuthor.shrink_to_fit();
|
||||
Serial.printf("[%lu] [FT] Cleared non-essential memory before File Transfer\n", millis());
|
||||
|
||||
enterNewActivity(new CrossPointWebServerActivity(renderer, mappedInputManager, onGoHome));
|
||||
}
|
||||
|
||||
@ -396,12 +405,24 @@ void onGoToClearCache() {
|
||||
|
||||
void onGoToMyLibrary() {
|
||||
exitActivity();
|
||||
|
||||
// Reload recent books if they were cleared (e.g., when exiting File Transfer mode)
|
||||
if (RECENT_BOOKS.getCount() == 0) {
|
||||
RECENT_BOOKS.loadFromFile();
|
||||
}
|
||||
|
||||
enterNewActivity(
|
||||
new MyLibraryActivity(renderer, mappedInputManager, onGoHome, onGoToReader, onGoToListView, onGoToBookmarkList));
|
||||
}
|
||||
|
||||
void onGoToMyLibraryWithTab(const std::string& path, MyLibraryActivity::Tab tab) {
|
||||
exitActivity();
|
||||
|
||||
// Reload recent books if they were cleared (e.g., when exiting File Transfer mode)
|
||||
if (RECENT_BOOKS.getCount() == 0) {
|
||||
RECENT_BOOKS.loadFromFile();
|
||||
}
|
||||
|
||||
enterNewActivity(new MyLibraryActivity(renderer, mappedInputManager, onGoHome, onGoToReader, onGoToListView,
|
||||
onGoToBookmarkList, tab, path));
|
||||
}
|
||||
@ -413,6 +434,12 @@ void onGoToBrowser() {
|
||||
|
||||
void onGoHome() {
|
||||
exitActivity();
|
||||
|
||||
// Reload recent books if they were cleared (e.g., when exiting File Transfer mode)
|
||||
if (RECENT_BOOKS.getCount() == 0) {
|
||||
RECENT_BOOKS.loadFromFile();
|
||||
}
|
||||
|
||||
enterNewActivity(new HomeActivity(renderer, mappedInputManager, onContinueReading, onGoToListsOrPinned,
|
||||
onGoToMyLibrary, onGoToSettings, onGoToFileTransfer, onGoToBrowser));
|
||||
}
|
||||
|
||||
@ -441,17 +441,33 @@ void CrossPointWebServer::handleFileListData() const {
|
||||
showHidden = server->arg("showHidden") == "true";
|
||||
}
|
||||
|
||||
// Check client connection before starting
|
||||
if (!server->client().connected()) {
|
||||
Serial.printf("[%lu] [WEB] Client disconnected before file list could start\n", millis());
|
||||
return;
|
||||
}
|
||||
|
||||
server->setContentLength(CONTENT_LENGTH_UNKNOWN);
|
||||
server->send(200, "application/json", "");
|
||||
server->sendContent("[");
|
||||
if (!sendContentSafe("[")) {
|
||||
Serial.printf("[%lu] [WEB] Client disconnected at start of file list\n", millis());
|
||||
return;
|
||||
}
|
||||
|
||||
char output[512];
|
||||
constexpr size_t outputSize = sizeof(output);
|
||||
bool seenFirst = false;
|
||||
bool clientDisconnected = false;
|
||||
JsonDocument doc;
|
||||
|
||||
scanFiles(
|
||||
currentPath.c_str(),
|
||||
[this, &output, &doc, seenFirst](const FileInfo& info) mutable {
|
||||
[this, &output, &doc, &seenFirst, &clientDisconnected](const FileInfo& info) mutable {
|
||||
// Skip remaining files if client already disconnected
|
||||
if (clientDisconnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
doc.clear();
|
||||
doc["name"] = info.name;
|
||||
doc["size"] = info.size;
|
||||
@ -475,18 +491,33 @@ void CrossPointWebServer::handleFileListData() const {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send comma separator before all entries except the first
|
||||
if (seenFirst) {
|
||||
server->sendContent(",");
|
||||
if (!sendContentSafe(",")) {
|
||||
clientDisconnected = true;
|
||||
Serial.printf("[%lu] [WEB] Client disconnected during file list\n", millis());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
seenFirst = true;
|
||||
}
|
||||
server->sendContent(output);
|
||||
|
||||
// Send the JSON entry with flow control
|
||||
if (!sendContentSafe(output)) {
|
||||
clientDisconnected = true;
|
||||
Serial.printf("[%lu] [WEB] Client disconnected during file list\n", millis());
|
||||
return;
|
||||
}
|
||||
},
|
||||
showHidden);
|
||||
server->sendContent("]");
|
||||
|
||||
// Only send closing bracket if client is still connected
|
||||
if (!clientDisconnected) {
|
||||
sendContentSafe("]");
|
||||
// End of streamed response, empty chunk to signal client
|
||||
server->sendContent("");
|
||||
Serial.printf("[%lu] [WEB] Served file listing page for path: %s\n", millis(), currentPath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Static variables for upload handling
|
||||
@ -1264,6 +1295,40 @@ void CrossPointWebServer::handleRename() const {
|
||||
}
|
||||
}
|
||||
|
||||
// Counter for flow control pacing
|
||||
static uint8_t sendContentCounter = 0;
|
||||
|
||||
bool CrossPointWebServer::sendContentSafe(const char* content) const {
|
||||
if (!server || !server->client().connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send the content
|
||||
server->sendContent(content);
|
||||
|
||||
// Flow control: give TCP stack time to transmit data and drain the send buffer
|
||||
// The ESP32 TCP buffer is limited and fills quickly when streaming many small chunks.
|
||||
// We use progressive delays:
|
||||
// - yield() after every send to allow WiFi processing
|
||||
// - delay(5ms) every send to allow buffer draining
|
||||
// - delay(50ms) every 10 sends to allow larger buffer flush
|
||||
yield();
|
||||
sendContentCounter++;
|
||||
|
||||
if (sendContentCounter >= 10) {
|
||||
sendContentCounter = 0;
|
||||
delay(50); // Longer pause every 10 sends for buffer catchup
|
||||
} else {
|
||||
delay(5); // Short pause each send
|
||||
}
|
||||
|
||||
return server->client().connected();
|
||||
}
|
||||
|
||||
bool CrossPointWebServer::sendContentSafe(const String& content) const {
|
||||
return sendContentSafe(content.c_str());
|
||||
}
|
||||
|
||||
bool CrossPointWebServer::copyFile(const String& srcPath, const String& destPath) const {
|
||||
FsFile srcFile;
|
||||
FsFile destFile;
|
||||
|
||||
@ -108,6 +108,11 @@ class CrossPointWebServer {
|
||||
bool copyFile(const String& srcPath, const String& destPath) const;
|
||||
bool copyFolder(const String& srcPath, const String& destPath) const;
|
||||
|
||||
// Helper for safe content sending with flow control
|
||||
// Returns false if client disconnected, true otherwise
|
||||
bool sendContentSafe(const char* content) const;
|
||||
bool sendContentSafe(const String& content) const;
|
||||
|
||||
// List management handlers
|
||||
void handleListGet() const;
|
||||
void handleListPost() const;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user