⚡ Full migration to esp-idf
This commit is contained in:
+77
-78
@@ -1,9 +1,12 @@
|
||||
#include <filesystem_ws.h>
|
||||
#include <filesystem.h>
|
||||
#include <pb_encode.h>
|
||||
#include <pb_decode.h>
|
||||
#include <esp_littlefs.h>
|
||||
#include <esp_timer.h>
|
||||
#include <esp_log.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cstring>
|
||||
|
||||
static const char* TAG = "FileSystemWS";
|
||||
|
||||
@@ -29,7 +32,7 @@ void FileSystemHandler::cleanupExpiredTransfers() {
|
||||
while (dlIt != downloads_.end()) {
|
||||
if (now - dlIt->second.lastActivityTime > FS_TRANSFER_TIMEOUT_MS) {
|
||||
if (dlIt->second.file) {
|
||||
dlIt->second.file.close();
|
||||
fclose(dlIt->second.file);
|
||||
}
|
||||
ESP_LOGW(TAG, "Download %u timed out", dlIt->first);
|
||||
|
||||
@@ -53,9 +56,9 @@ void FileSystemHandler::cleanupExpiredTransfers() {
|
||||
while (ulIt != uploads_.end()) {
|
||||
if (now - ulIt->second.lastActivityTime > FS_TRANSFER_TIMEOUT_MS) {
|
||||
if (ulIt->second.file) {
|
||||
ulIt->second.file.close();
|
||||
fclose(ulIt->second.file);
|
||||
}
|
||||
ESP_FS.remove(ulIt->second.path.c_str());
|
||||
remove(ulIt->second.path.c_str());
|
||||
ESP_LOGW(TAG, "Upload %u timed out, deleted partial file", ulIt->first);
|
||||
|
||||
if (sendUploadCompleteCallback_) {
|
||||
@@ -77,10 +80,11 @@ void FileSystemHandler::cleanupExpiredTransfers() {
|
||||
socket_message_FSDeleteResponse FileSystemHandler::handleDelete(const socket_message_FSDeleteRequest& req) {
|
||||
socket_message_FSDeleteResponse response = socket_message_FSDeleteResponse_init_zero;
|
||||
|
||||
std::string path(req.path);
|
||||
std::string path = std::string(MOUNT_POINT) + req.path;
|
||||
ESP_LOGI(TAG, "Delete request: %s", path.c_str());
|
||||
|
||||
if (!ESP_FS.exists(path.c_str())) {
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) != 0) {
|
||||
response.success = false;
|
||||
strncpy(response.error, "File or directory not found", sizeof(response.error) - 1);
|
||||
return response;
|
||||
@@ -97,41 +101,45 @@ socket_message_FSDeleteResponse FileSystemHandler::handleDelete(const socket_mes
|
||||
}
|
||||
|
||||
bool FileSystemHandler::deleteRecursive(const std::string& path) {
|
||||
File file = ESP_FS.open(path.c_str());
|
||||
if (!file) return false;
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) != 0) return false;
|
||||
|
||||
if (file.isDirectory()) {
|
||||
File child = file.openNextFile();
|
||||
while (child) {
|
||||
std::string childPath = std::string(child.name());
|
||||
child.close();
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
DIR* dir = opendir(path.c_str());
|
||||
if (!dir) return false;
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
std::string childPath = path + "/" + entry->d_name;
|
||||
if (!deleteRecursive(childPath)) {
|
||||
file.close();
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
child = file.openNextFile();
|
||||
}
|
||||
file.close();
|
||||
return ESP_FS.rmdir(path.c_str());
|
||||
closedir(dir);
|
||||
return rmdir(path.c_str()) == 0;
|
||||
} else {
|
||||
file.close();
|
||||
return ESP_FS.remove(path.c_str());
|
||||
return remove(path.c_str()) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
socket_message_FSMkdirResponse FileSystemHandler::handleMkdir(const socket_message_FSMkdirRequest& req) {
|
||||
socket_message_FSMkdirResponse response = socket_message_FSMkdirResponse_init_zero;
|
||||
|
||||
std::string path(req.path);
|
||||
std::string path = std::string(MOUNT_POINT) + req.path;
|
||||
ESP_LOGI(TAG, "Mkdir request: %s", path.c_str());
|
||||
|
||||
if (ESP_FS.exists(path.c_str())) {
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) == 0) {
|
||||
response.success = false;
|
||||
strncpy(response.error, "Path already exists", sizeof(response.error) - 1);
|
||||
return response;
|
||||
}
|
||||
|
||||
if (ESP_FS.mkdir(path.c_str())) {
|
||||
if (FileSystem::mkdirRecursive(path.c_str())) {
|
||||
response.success = true;
|
||||
} else {
|
||||
response.success = false;
|
||||
@@ -142,32 +150,40 @@ socket_message_FSMkdirResponse FileSystemHandler::handleMkdir(const socket_messa
|
||||
}
|
||||
|
||||
void FileSystemHandler::listDirectory(const std::string& path, socket_message_FSListResponse& response) {
|
||||
File dir = ESP_FS.open(path.c_str());
|
||||
if (!dir || !dir.isDirectory()) {
|
||||
DIR* dir = opendir(path.c_str());
|
||||
if (!dir) {
|
||||
return;
|
||||
}
|
||||
|
||||
File file = dir.openNextFile();
|
||||
struct dirent* entry;
|
||||
int fileCount = 0;
|
||||
int dirCount = 0;
|
||||
|
||||
while (file && fileCount < 20 && dirCount < 20) { // Limit to match protobuf max_count
|
||||
if (file.isDirectory()) {
|
||||
while ((entry = readdir(dir)) != nullptr && fileCount < 20 && dirCount < 20) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string fullPath = path + "/" + entry->d_name;
|
||||
struct stat st;
|
||||
if (stat(fullPath.c_str(), &st) != 0) continue;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (dirCount < 20) {
|
||||
strncpy(response.directories[dirCount].name, file.name(),
|
||||
strncpy(response.directories[dirCount].name, entry->d_name,
|
||||
sizeof(response.directories[dirCount].name) - 1);
|
||||
dirCount++;
|
||||
}
|
||||
} else {
|
||||
if (fileCount < 20) {
|
||||
strncpy(response.files[fileCount].name, file.name(), sizeof(response.files[fileCount].name) - 1);
|
||||
response.files[fileCount].size = file.size();
|
||||
strncpy(response.files[fileCount].name, entry->d_name, sizeof(response.files[fileCount].name) - 1);
|
||||
response.files[fileCount].size = st.st_size;
|
||||
fileCount++;
|
||||
}
|
||||
}
|
||||
file = dir.openNextFile();
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
response.files_count = fileCount;
|
||||
response.directories_count = dirCount;
|
||||
}
|
||||
@@ -175,12 +191,15 @@ void FileSystemHandler::listDirectory(const std::string& path, socket_message_FS
|
||||
socket_message_FSListResponse FileSystemHandler::handleList(const socket_message_FSListRequest& req) {
|
||||
socket_message_FSListResponse response = socket_message_FSListResponse_init_zero;
|
||||
|
||||
std::string path(req.path);
|
||||
if (path.empty()) path = "/";
|
||||
std::string path = std::string(MOUNT_POINT);
|
||||
if (strlen(req.path) > 0 && req.path[0] != '\0') {
|
||||
path += req.path;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "List request: %s", path.c_str());
|
||||
|
||||
if (!ESP_FS.exists(path.c_str())) {
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) != 0) {
|
||||
response.success = false;
|
||||
strncpy(response.error, "Path not found", sizeof(response.error) - 1);
|
||||
return response;
|
||||
@@ -192,14 +211,12 @@ socket_message_FSListResponse FileSystemHandler::handleList(const socket_message
|
||||
return response;
|
||||
}
|
||||
|
||||
// ===== STREAMING DOWNLOAD =====
|
||||
|
||||
void FileSystemHandler::handleDownloadRequest(const socket_message_FSDownloadRequest& req, int clientId) {
|
||||
std::string path(req.path);
|
||||
std::string path = std::string(MOUNT_POINT) + req.path;
|
||||
ESP_LOGI(TAG, "Download request: %s", path.c_str());
|
||||
|
||||
// Validate file exists
|
||||
if (!ESP_FS.exists(path.c_str())) {
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) != 0 || S_ISDIR(st.st_mode)) {
|
||||
if (sendMetadataCallback_) {
|
||||
socket_message_FSDownloadMetadata metadata = socket_message_FSDownloadMetadata_init_zero;
|
||||
metadata.success = false;
|
||||
@@ -209,8 +226,8 @@ void FileSystemHandler::handleDownloadRequest(const socket_message_FSDownloadReq
|
||||
return;
|
||||
}
|
||||
|
||||
File file = ESP_FS.open(path.c_str(), "r");
|
||||
if (!file || file.isDirectory()) {
|
||||
FILE* file = fopen(path.c_str(), "rb");
|
||||
if (!file) {
|
||||
if (sendMetadataCallback_) {
|
||||
socket_message_FSDownloadMetadata metadata = socket_message_FSDownloadMetadata_init_zero;
|
||||
metadata.success = false;
|
||||
@@ -220,11 +237,7 @@ void FileSystemHandler::handleDownloadRequest(const socket_message_FSDownloadReq
|
||||
return;
|
||||
}
|
||||
|
||||
// Set file buffer size large, so we use ram to read from
|
||||
// Set buffer size so 1 mb (static) TODO: Check that there is enough ram to do this
|
||||
file.setBufferSize(1000000);
|
||||
|
||||
uint32_t fileSize = file.size();
|
||||
uint32_t fileSize = st.st_size;
|
||||
uint32_t chunkSize = FS_MAX_CHUNK_SIZE;
|
||||
uint32_t totalChunks = (fileSize + chunkSize - 1) / chunkSize;
|
||||
if (totalChunks == 0) totalChunks = 1;
|
||||
@@ -254,10 +267,8 @@ void FileSystemHandler::handleDownloadRequest(const socket_message_FSDownloadReq
|
||||
|
||||
ESP_LOGI(TAG, "Download started: %s, size=%u, chunks=%u, id=%u", path.c_str(), fileSize, totalChunks, transferId);
|
||||
|
||||
// Start streaming chunks immediately
|
||||
while (sendNextDownloadChunk(transferId)) {
|
||||
// Keep sending until done or error
|
||||
taskYIELD(); // Allow other tasks to run
|
||||
taskYIELD();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +291,7 @@ bool FileSystemHandler::sendNextDownloadChunk(uint32_t transferId) {
|
||||
sendCompleteCallback_(complete, state.clientId);
|
||||
}
|
||||
|
||||
state.file.close();
|
||||
fclose(state.file);
|
||||
downloads_.erase(it);
|
||||
ESP_LOGI(TAG, "Download completed: %u", transferId);
|
||||
return false;
|
||||
@@ -297,7 +308,7 @@ bool FileSystemHandler::sendNextDownloadChunk(uint32_t transferId) {
|
||||
bytesToRead = state.fileSize - position;
|
||||
}
|
||||
|
||||
size_t bytesRead = state.file.read(data->data.bytes, bytesToRead);
|
||||
size_t bytesRead = fread(data->data.bytes, 1, bytesToRead, state.file);
|
||||
if (bytesRead == 0 && bytesToRead > 0) {
|
||||
delete data;
|
||||
if (sendCompleteCallback_) {
|
||||
@@ -310,7 +321,7 @@ bool FileSystemHandler::sendNextDownloadChunk(uint32_t transferId) {
|
||||
sendCompleteCallback_(complete, state.clientId);
|
||||
}
|
||||
|
||||
state.file.close();
|
||||
fclose(state.file);
|
||||
downloads_.erase(it);
|
||||
ESP_LOGE(TAG, "Download failed - read error: %u", transferId);
|
||||
return false;
|
||||
@@ -328,45 +339,40 @@ bool FileSystemHandler::sendNextDownloadChunk(uint32_t transferId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ===== STREAMING UPLOAD =====
|
||||
|
||||
socket_message_FSUploadStartResponse FileSystemHandler::handleUploadStart(const socket_message_FSUploadStart& req,
|
||||
int clientId) {
|
||||
socket_message_FSUploadStartResponse response = socket_message_FSUploadStartResponse_init_zero;
|
||||
|
||||
std::string path(req.path);
|
||||
std::string path = std::string(MOUNT_POINT) + req.path;
|
||||
ESP_LOGI(TAG, "Upload start request: %s, size=%u, chunks=%u", path.c_str(), req.file_size, req.total_chunks);
|
||||
|
||||
// Check available space
|
||||
size_t fs_total = 0, fs_used = 0;
|
||||
esp_littlefs_info("spiffs", &fs_total, &fs_used);
|
||||
size_t freeSpace = fs_total - fs_used;
|
||||
if (freeSpace < req.file_size + 4096) { // 4KB safety margin
|
||||
if (freeSpace < req.file_size + 4096) {
|
||||
response.success = false;
|
||||
strncpy(response.error, "Insufficient storage space", sizeof(response.error) - 1);
|
||||
return response;
|
||||
}
|
||||
|
||||
// Ensure parent directory exists
|
||||
size_t lastSlash = path.find_last_of('/');
|
||||
if (lastSlash != std::string::npos && lastSlash > 0) {
|
||||
std::string parentDir = path.substr(0, lastSlash);
|
||||
if (!ESP_FS.exists(parentDir.c_str())) {
|
||||
struct stat st;
|
||||
if (stat(parentDir.c_str(), &st) != 0) {
|
||||
response.success = false;
|
||||
strncpy(response.error, "Parent directory does not exist", sizeof(response.error) - 1);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
File file = ESP_FS.open(path.c_str(), FILE_WRITE);
|
||||
FILE* file = fopen(path.c_str(), "wb");
|
||||
if (!file) {
|
||||
response.success = false;
|
||||
strncpy(response.error, "Cannot open file for writing", sizeof(response.error) - 1);
|
||||
return response;
|
||||
}
|
||||
|
||||
file.setBufferSize(1000000);
|
||||
|
||||
uint32_t transferId = generateTransferId();
|
||||
|
||||
UploadState state;
|
||||
@@ -402,20 +408,15 @@ void FileSystemHandler::handleUploadData(const socket_message_FSUploadData& req)
|
||||
UploadState& state = it->second;
|
||||
state.lastActivityTime = esp_timer_get_time() / 1000;
|
||||
|
||||
// Skip if we already have an error
|
||||
if (state.hasError) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate chunk index (allow out-of-order but warn)
|
||||
if (req.chunk_index != state.chunksReceived) {
|
||||
ESP_LOGW(TAG, "Upload chunk out of order: expected %u, got %u", state.chunksReceived, req.chunk_index);
|
||||
// For now, we'll accept it anyway and write sequentially
|
||||
// A more robust implementation would buffer out-of-order chunks
|
||||
}
|
||||
|
||||
// Write chunk data
|
||||
size_t bytesWritten = state.file.write(req.data.bytes, req.data.size);
|
||||
size_t bytesWritten = fwrite(req.data.bytes, 1, req.data.size, state.file);
|
||||
if (bytesWritten != req.data.size) {
|
||||
state.hasError = true;
|
||||
state.errorMessage = "Failed to write chunk";
|
||||
@@ -426,15 +427,13 @@ void FileSystemHandler::handleUploadData(const socket_message_FSUploadData& req)
|
||||
state.chunksReceived++;
|
||||
state.bytesReceived += bytesWritten;
|
||||
|
||||
// Flush periodically to prevent LittleFS buffer issues
|
||||
if (state.chunksReceived > 0 && state.chunksReceived % 64 == 0) {
|
||||
ESP_LOGD(TAG, "Flushing file at chunk %u", state.chunksReceived);
|
||||
state.file.flush();
|
||||
fflush(state.file);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Upload chunk %u/%u: %u bytes", state.chunksReceived, state.totalChunks, bytesWritten);
|
||||
|
||||
// Check if upload is complete
|
||||
if (state.chunksReceived >= state.totalChunks) {
|
||||
finalizeUpload(transferId, true);
|
||||
}
|
||||
@@ -449,11 +448,11 @@ void FileSystemHandler::finalizeUpload(uint32_t transferId, bool success, const
|
||||
UploadState& state = it->second;
|
||||
|
||||
if (state.file) {
|
||||
state.file.close();
|
||||
fclose(state.file);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
ESP_FS.remove(state.path.c_str());
|
||||
remove(state.path.c_str());
|
||||
ESP_LOGW(TAG, "Upload failed, deleted partial file: %s", state.path.c_str());
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Upload completed: %s (%u bytes)", state.path.c_str(), state.bytesReceived);
|
||||
@@ -473,8 +472,6 @@ void FileSystemHandler::finalizeUpload(uint32_t transferId, bool success, const
|
||||
uploads_.erase(it);
|
||||
}
|
||||
|
||||
// ===== TRANSFER CONTROL =====
|
||||
|
||||
socket_message_FSCancelTransferResponse FileSystemHandler::handleCancelTransfer(
|
||||
const socket_message_FSCancelTransfer& req) {
|
||||
socket_message_FSCancelTransferResponse response = socket_message_FSCancelTransferResponse_init_zero;
|
||||
@@ -484,7 +481,7 @@ socket_message_FSCancelTransferResponse FileSystemHandler::handleCancelTransfer(
|
||||
auto dlIt = downloads_.find(transferId);
|
||||
if (dlIt != downloads_.end()) {
|
||||
if (dlIt->second.file) {
|
||||
dlIt->second.file.close();
|
||||
fclose(dlIt->second.file);
|
||||
}
|
||||
downloads_.erase(dlIt);
|
||||
response.success = true;
|
||||
@@ -495,9 +492,9 @@ socket_message_FSCancelTransferResponse FileSystemHandler::handleCancelTransfer(
|
||||
auto ulIt = uploads_.find(transferId);
|
||||
if (ulIt != uploads_.end()) {
|
||||
if (ulIt->second.file) {
|
||||
ulIt->second.file.close();
|
||||
fclose(ulIt->second.file);
|
||||
}
|
||||
ESP_FS.remove(ulIt->second.path.c_str());
|
||||
remove(ulIt->second.path.c_str());
|
||||
uploads_.erase(ulIt);
|
||||
response.success = true;
|
||||
ESP_LOGI(TAG, "Upload cancelled: %u", transferId);
|
||||
@@ -508,4 +505,6 @@ socket_message_FSCancelTransferResponse FileSystemHandler::handleCancelTransfer(
|
||||
return response;
|
||||
}
|
||||
|
||||
void FileSystemHandler::processPendingDownloads() {}
|
||||
|
||||
} // namespace FileSystemWS
|
||||
|
||||
Reference in New Issue
Block a user