add ability to send File objects directly
utilize request's tempFile to store a handle to the file to send use open instead of exists in static handler
This commit is contained in:
parent
16a6016759
commit
3709dd5e14
|
@ -186,12 +186,14 @@ class AsyncWebServerRequest {
|
|||
void send(AsyncWebServerResponse *response);
|
||||
void send(int code, String contentType=String(), String content=String());
|
||||
void send(FS &fs, String path, String contentType=String(), bool download=false);
|
||||
void send(File content, String contentType=String(), bool download=false);
|
||||
void send(Stream &stream, String contentType, size_t len);
|
||||
void send(String contentType, size_t len, AwsResponseFiller callback);
|
||||
void sendChunked(String contentType, AwsResponseFiller callback);
|
||||
|
||||
AsyncWebServerResponse *beginResponse(int code, String contentType=String(), String content=String());
|
||||
AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
||||
AsyncWebServerResponse *beginResponse(File content, String contentType=String(), bool download=false);
|
||||
AsyncWebServerResponse *beginResponse(Stream &stream, String contentType, size_t len);
|
||||
AsyncWebServerResponse *beginResponse(String contentType, size_t len, AwsResponseFiller callback);
|
||||
AsyncWebServerResponse *beginChunkedResponse(String contentType, AwsResponseFiller callback);
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
|
||||
class AsyncStaticWebHandler: public AsyncWebHandler {
|
||||
private:
|
||||
String _getPath(AsyncWebServerRequest *request, const bool withStats);
|
||||
bool _fileExists(const String path, const bool withStats);
|
||||
bool _getFile(AsyncWebServerRequest *request);
|
||||
bool _fileExists(AsyncWebServerRequest *request, const String path);
|
||||
uint8_t _countBits(const uint8_t value);
|
||||
protected:
|
||||
FS _fs;
|
||||
|
@ -39,28 +39,7 @@ class AsyncStaticWebHandler: public AsyncWebHandler {
|
|||
uint8_t _gzipStats;
|
||||
uint8_t _fileStats;
|
||||
public:
|
||||
AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
|
||||
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header){
|
||||
// Ensure leading '/'
|
||||
if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
|
||||
if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
|
||||
|
||||
// If uri or path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||
// However - if they both do not end '/' we, can't assume they are files, they can still be directory.
|
||||
bool isUriDir = _uri[_uri.length()-1] == '/';
|
||||
bool isPathDir = _path[_path.length()-1] == '/';
|
||||
_isDir = isUriDir || isPathDir;
|
||||
|
||||
// If we serving directory - remove the trailing '/' so we can handle default file
|
||||
// Notice that root will be "" not "/"
|
||||
if (_isDir && isUriDir) _uri = _uri.substring(0, _uri.length()-1);
|
||||
if (_isDir && isPathDir) _path = _path.substring(0, _path.length()-1);
|
||||
|
||||
// Reset stats
|
||||
_gzipFirst = false;
|
||||
_gzipStats = 0;
|
||||
_fileStats = 0;
|
||||
}
|
||||
AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header);
|
||||
bool canHandle(AsyncWebServerRequest *request);
|
||||
void handleRequest(AsyncWebServerRequest *request);
|
||||
};
|
||||
|
|
|
@ -21,21 +21,44 @@
|
|||
#include "ESPAsyncWebServer.h"
|
||||
#include "WebHandlerImpl.h"
|
||||
|
||||
AsyncStaticWebHandler::AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
|
||||
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header)
|
||||
{
|
||||
// Ensure leading '/'
|
||||
if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
|
||||
if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
|
||||
|
||||
// If uri or path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||
// However - if they both do not end '/' we, can't assume they are files, they can still be directory.
|
||||
bool isUriDir = _uri[_uri.length()-1] == '/';
|
||||
bool isPathDir = _path[_path.length()-1] == '/';
|
||||
_isDir = isUriDir || isPathDir;
|
||||
|
||||
// If we serving directory - remove the trailing '/' so we can handle default file
|
||||
// Notice that root will be "" not "/"
|
||||
if (_isDir && isUriDir) _uri = _uri.substring(0, _uri.length()-1);
|
||||
if (_isDir && isPathDir) _path = _path.substring(0, _path.length()-1);
|
||||
|
||||
// Reset stats
|
||||
_gzipFirst = false;
|
||||
_gzipStats = 0;
|
||||
_fileStats = 0;
|
||||
}
|
||||
|
||||
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request)
|
||||
{
|
||||
if (request->method() == HTTP_GET &&
|
||||
request->url().startsWith(_uri) &&
|
||||
_getPath(request, true).length()) {
|
||||
_getFile(request)) {
|
||||
|
||||
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String AsyncStaticWebHandler::_getPath(AsyncWebServerRequest *request, const bool withStats)
|
||||
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
|
||||
{
|
||||
// Remove the found uri
|
||||
String path = request->url().substring(_uri.length());
|
||||
|
@ -46,19 +69,18 @@ String AsyncStaticWebHandler::_getPath(AsyncWebServerRequest *request, const boo
|
|||
path = _path + path;
|
||||
|
||||
// Do we have a file or .gz file
|
||||
if (!canSkipFileCheck) if (_fileExists(path, withStats)) return path;
|
||||
if (!canSkipFileCheck && _fileExists(request, path))
|
||||
return true;
|
||||
|
||||
// Try to add default page, ensure there is a trailing '/' ot the path.
|
||||
if (path.length() == 0 || path[path.length()-1] != '/') path += "/";
|
||||
if (path.length() == 0 || path[path.length()-1] != '/')
|
||||
path += "/";
|
||||
path += "index.htm";
|
||||
|
||||
if (_fileExists(path, withStats)) return path;
|
||||
|
||||
// No file - return empty string
|
||||
return String();
|
||||
return_fileExists(request, path);
|
||||
}
|
||||
|
||||
bool AsyncStaticWebHandler::_fileExists(const String path, const bool withStats)
|
||||
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String path)
|
||||
{
|
||||
bool fileFound = false;
|
||||
bool gzipFound = false;
|
||||
|
@ -66,16 +88,24 @@ bool AsyncStaticWebHandler::_fileExists(const String path, const bool withStats)
|
|||
String gzip = path + ".gz";
|
||||
|
||||
if (_gzipFirst) {
|
||||
gzipFound = _fs.exists(gzip);
|
||||
if (!gzipFound) fileFound = _fs.exists(path);
|
||||
request->_tempFile = _fs.open(gzip, "r");
|
||||
gzipFound = request->_tempFile == true;
|
||||
if (!gzipFound){
|
||||
request->_tempFile = _fs.open(path, "r");
|
||||
fileFound = request->_tempFile == true;
|
||||
}
|
||||
} else {
|
||||
fileFound = _fs.exists(path);
|
||||
if (!fileFound) gzipFound = _fs.exists(gzip);
|
||||
request->_tempFile = _fs.open(path, "r");
|
||||
fileFound = request->_tempFile == true;
|
||||
if (!fileFound){
|
||||
request->_tempFile = _fs.open(gzip, "r");
|
||||
gzipFound = request->_tempFile == true;
|
||||
}
|
||||
}
|
||||
|
||||
bool found = fileFound || gzipFound;
|
||||
|
||||
if (withStats && found) {
|
||||
if (found) {
|
||||
_gzipStats = (_gzipStats << 1) + gzipFound ? 1 : 0;
|
||||
_fileStats = (_fileStats << 1) + fileFound ? 1 : 0;
|
||||
_gzipFirst = _countBits(_gzipStats) > _countBits(_fileStats);
|
||||
|
@ -94,15 +124,12 @@ uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value)
|
|||
|
||||
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
|
||||
{
|
||||
String path = _getPath(request, false);
|
||||
|
||||
if (path.length()) {
|
||||
AsyncWebServerResponse * response = new AsyncFileResponse(_fs, path);
|
||||
if (request->_tempFile == true) {
|
||||
AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile);
|
||||
if (_cache_header.length() != 0)
|
||||
response->addHeader("Cache-Control", _cache_header);
|
||||
request->send(response);
|
||||
} else {
|
||||
request->send(404);
|
||||
}
|
||||
path = String();
|
||||
}
|
||||
|
|
|
@ -651,6 +651,12 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, String pat
|
|||
return NULL;
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, String contentType, bool download){
|
||||
if(content == true)
|
||||
return new AsyncFileResponse(content, contentType, download);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, String contentType, size_t len){
|
||||
return new AsyncStreamResponse(stream, contentType, len);
|
||||
}
|
||||
|
@ -679,6 +685,12 @@ void AsyncWebServerRequest::send(FS &fs, String path, String contentType, bool d
|
|||
} else send(404);
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::send(File content, String contentType, bool download){
|
||||
if(content == true){
|
||||
send(beginResponse(content, contentType, download));
|
||||
} else send(404);
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::send(Stream &stream, String contentType, size_t len){
|
||||
send(beginResponse(stream, contentType, len));
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ class AsyncFileResponse: public AsyncAbstractResponse {
|
|||
void _setContentType(String path);
|
||||
public:
|
||||
AsyncFileResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
||||
AsyncFileResponse(File content, String contentType=String(), bool download=false);
|
||||
~AsyncFileResponse();
|
||||
bool _sourceValid(){ return !!(_content); }
|
||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen);
|
||||
|
|
|
@ -376,6 +376,31 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, String path, String contentType, bo
|
|||
_contentLength = _content.size();
|
||||
}
|
||||
|
||||
AsyncFileResponse::AsyncFileResponse(File content, String contentType, bool download){
|
||||
_code = 200;
|
||||
_content = content;
|
||||
_path = String(_content.name());
|
||||
_contentLength = _content.size();
|
||||
int filenameStart = path.lastIndexOf('/') + 1;
|
||||
char buf[26+path.length()-filenameStart];
|
||||
char* filename = (char*)path.c_str() + filenameStart;
|
||||
|
||||
if(!download && _path.endsWith(".gz"))
|
||||
addHeader("Content-Encoding", "gzip");
|
||||
|
||||
if(contentType == "")
|
||||
_setContentType(_path);
|
||||
else
|
||||
_contentType = contentType;
|
||||
|
||||
if(download) {
|
||||
snprintf(buf, sizeof (buf), "attachment; filename='%s'", filename);
|
||||
} else {
|
||||
snprintf(buf, sizeof (buf), "inline; filename='%s'", filename);
|
||||
}
|
||||
addHeader("Content-Disposition", buf);
|
||||
}
|
||||
|
||||
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
_content.read(data, len);
|
||||
return len;
|
||||
|
|
Loading…
Reference in New Issue