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:
Me No Dev 2016-06-17 01:43:59 +03:00
parent 16a6016759
commit 3709dd5e14
6 changed files with 90 additions and 44 deletions

View File

@ -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);

View File

@ -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);
};

View File

@ -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();
}

View File

@ -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));
}

View File

@ -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);

View File

@ -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;