对于WebServer来说静态文件也是非常重要的一块。通常一个网页有很多文件组成,比如一个主页通常由index.html、favicon.ico等多个文件组成,用户访问 /index.html 时,浏览器接收到 index.html 文件后还会再请求该文件中关联的其它文件,这些文件名称和类型等都是无法预料的,一条条添加对应的url就非常不方便了,这个时候就需要用到静态文件服务了。
静态文件中通常可以使用模板引擎方便的实现界面与数据的分离,减少开发过程中的工作量,特定情景下还是蛮好用的。
这篇文章将对上面两块相关内容进行介绍。
本文中各例程演示均在ESP32中进行。
使用静态文件服务首先需要启动相关的文件系统,除了可以使用Flash上的SPIFFS系统,还可以使用SD卡等。初始完成后就可以用 AsyncWebServer
对象的 serveStatic()
方法来设置静态文件服务了,比如下面这样:
// 用户访问/page.htm时,返回SPIFFS中/www/page.htm文件
server.serveStatic("/page.htm", SPIFFS, "/www/page.htm");
上面的方式指定了文件到文件,事实上大多数情况是不会这么用的,而是下面这样:
// 用户访问/目录下文件时返回SPIFFS中/www/路径下同名文件
// 比如用户访问/page.htm时,将会返回SPIFFS中/www/page.htm文件
// 比如用户访问/favicon.ico时,将会返回SPIFFS中/www/favicon.ico文件
// ……
// 特殊情况:用户访问/时,默认将会返回SPIFFS中/www/index.htm文件(如果存在)
server.serveStatic("/", SPIFFS, "/www/");
上面上面的使用方式是最常用的方式,上面的方式中有一种特殊情况,就是用户访问根目录时返回了index.htm文件,这个默认返回的文件也可以手动定义,使用下面方法:
// 用户访问/时,默认返回将变成SPIFFS中/www/default.html文件
server.serveStatic("/", SPIFFS, "/www/").setDefaultFile("default.html");
客户端如果多次打开网页,每次都从服务器获取静态文件的话对性能影响还是蛮大的,这时候就可以使用Cache-Control和Last-Modified来优化性能。
Cache-Control可以设定一个时间,客户端在获取到某个文件后将文件进行缓存,这段时间内如果再需要这个文件的话将不再从服务器获取,而是直接使用缓存。下面是Cache-Control的使用示例:
// 设定缓存事件600秒,客户端获取过的文件将在客户端缓存600秒
// 600秒内需要已缓存的文件将直接使用缓存,不再从服务器获取
server.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=600");
// 使用下面方式就可以随时更改Cache-Control
AsyncStaticWebHandler* handler = &server.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=600");
handler->setCacheControl("max-age=30");
Cache-Control除了上面功能外还有很多别的功能,不同功能下setCacheControl中填入的参数不同,比如填入 "no-store"
参数的话将禁用所有缓存。Cache-Control参数更多内容可以参考 Cache-Control 。
Last-Modified将设置一个时间参数,客户端如果缓存过文件,下次再需要该文件向服务器请求时将附带缓存文件的时间参数,服务器比较两个参数,如果服务器中的参数较新的话将发送新的文件,否则客户端将使用缓存文件。下面是Last-Modified的使用示例:
// 设定文件最后修改时间参数
AsyncStaticWebHandler* handler = &server.serveStatic("/", SPIFFS, "/www/").setLastModified("Mon, 26 Apr 2010 13:22:17 GMT");
Mon, 26 Apr 2010 13:22:17 GMT
// 更新文件最后修改时间参数
handler->setLastModified("Mon, 20 Jun 2016 14:00:00 GMT");
静态文件服务可以设置身份认证,用户必须通过认证后才能访问其中的文件:
// setAuthentication()中分别填入用户名和密码
server.serveStatic("/", SPIFFS, "/www/").setAuthentication("user", "pass");
除了上面的功能外静态文件服务还可以应用过滤器和模板引擎。下面是应用过滤器的示例(模板引擎的示例将在模板引擎章节介绍):
server.serveStatic("/", SPIFFS, "/www/").setFilter(ON_STA_FILTER); // ESP32处于STA模式时起效
server.serveStatic("/", SPIFFS, "/ap/").setFilter(ON_AP_FILTER); // ESP32处于AP模式时起效
从上面的介绍可以看到静态文件服务可选的参数非常多,如果需要设置多个参数的话可以使用下面两种方式:
// 方式一
server.serveStatic("/", SPIFFS, "/www/").setDefaultFile("default.html").setCacheControl("max-age=600");
// 方式二
AsyncStaticWebHandler* handler = &server.serveStatic("/", SPIFFS, "/www/");
handler->setDefaultFile("default.html");
handler->setCacheControl("max-age=600");
这里以使用SPIFFS作为文件系统进行静态文件服务功能演示,首先准备几个文件。将下面文本保存为名为index.html的文件:
<head>
<meta charset="UTF-8">
<title>ServeStatic Testtitle>
<link rel="stylesheet" type="text/css" href="mystyle.css">
head>
<body>
<p>这是静态文件服务测试p>
<p>本页面的背景颜色是由mystyle.css文件提供的p>
<p>本页面引用了mystyle.css文件,浏览器在解析时会自动向服务器请求该文件p>
body>
html>
将下面文本保存为名为mystyle.css的文件:
body {
background-color: gray;
}
在Arduino中使用下面代码:
#include
#include
#include //引入相应库
const char *ssid = "********";
const char *password = "********";
AsyncWebServer server(80); //声明WebServer对象
void setup()
{
Serial.begin(115200);
Serial.println();
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("Connected");
Serial.print("IP Address:");
Serial.println(WiFi.localIP());
SPIFFS.begin(true); //挂载SPIFFS,如果挂载失败则格式化生成SPIFFS,格式化时间较长
//当然上面begin中不填true格式化也行,下面演示中用工具上传文件时会自动生成SPIFFS
server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");
server.begin(); //启动服务器
Serial.println("Web server started");
}
void loop(){
}
上面使用了名为 ESP32 Sketch Data Upload
的工具来将 项目文件夹下data文件夹中 的网页相关文件上传到ESP32的SPIFFS中。一切就绪后在浏览器中访问就可以看到网页被正确的加载出来了,单独访问两个文件也没问题。
上传文件用到的工具项目地址如下:
https://github.com/me-no-dev/arduino-esp32fs-plugin
从项目地址下载Release版本压缩包,将其解压到Arduino安装文件夹tools文件夹下,最终文件路径为: .../Arduino/tools/ESP32FS/tool/esp32fs.jar
。重启Arduino IDE后就可以在菜单中看到选项。
ESP8266上传文件到SPIFFS的工具项目地址如下:
https://github.com/esp8266/arduino-esp8266fs-plugin
ESPAsyncWebServer库中有一个SPIFFSEditor功能,可以通过网页管理和编辑文件系统中的文件,这里做个简单的使用演示:
#include
#include
#include //引入相应库
#include //引入相应库
const char *ssid = "********";
const char *password = "********";
AsyncWebServer server(80); //声明WebServer对象
const char* http_username = "admin";
const char* http_password = "123456";
void setup()
{
Serial.begin(115200);
Serial.println();
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("Connected");
Serial.print("IP Address:");
Serial.println(WiFi.localIP());
SPIFFS.begin(true);
server.addHandler(new SPIFFSEditor(SPIFFS, http_username,http_password));
server.begin(); //启动服务器
Serial.println("Web server started");
}
void loop(){
}
上面就是SPIFFSEditor的演示了,需要注意的是SPIFFSEditor的网页中用到了外部的文件,需要能连上互联网才能正确加载。
ESPAsyncWebServer中的模板引擎目前只支持一个功能,实际值替换占位符。该模板引擎使用 %
来标识占位符,先看下面演示:
#include
#include //引入相应库
const char *ssid = "********";
const char *password = "********";
AsyncWebServer server(80); //声明WebServer对象
int count = 0;
String processor(const String& var)
{
if(var == "VAR1")
{
count++;
return String(count);
}
else if(var == "VAR2")
{
return "lalala~~~";
}
else if(var == "VAR3")
{
return F("ABCDEFG"); // F() 是Arduino的PROGMEM机制,里面的字符串将存放在Flash中,减少对内存的占用
}
return String();
}
void handleRoot(AsyncWebServerRequest *request) //回调函数
{
// 向客户端发送响应和内容
// 注意文本中用 % % 包围的字符串,
request->send_P(200, "text/plain", "count = %VAR1%, VAR2 = %VAR2%, VAR3 = %VAR3%", processor);
}
void setup()
{
Serial.begin(115200);
Serial.println();
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("Connected");
Serial.print("IP Address:");
Serial.println(WiFi.localIP());
server.on("/", HTTP_GET, handleRoot); //注册链接"/"与对应回调函数
server.begin(); //启动服务器
Serial.println("Web server started");
}
void loop(){
}
ESPAsyncWebServer的模板引擎因为功能单一,从上面的演示中就可以完全理解了,模板引擎经常用在静态的文件中。除了上面演示用的方式还可以用下面的方式来使用:
const char index_html[] PROGMEM = "..."; // large char array, tested with 14k
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html);
request->send(response);
或者直接在静态文件服务上使用也可以:
String processor(const String& var)
{
if(var == "HELLO_FROM_TEMPLATE")
return F("Hello world!");
return String();
}
// ...
server.serveStatic("/", SPIFFS, "/www/").setTemplateProcessor(processor);
ESPAsyncWebServer静态文件和模板引擎主要就是上面这些内容,更多内容可以查看项目官方的文档和例程,或者我的其他ESPAsyncWebServer的文章:
ESPAsyncWebServer项目地址:https://github.com/me-no-dev/ESPAsyncWebServer
《Arduino for ESP8266&ESP32适用库ESPAsyncWebServer:快速入门》
《Arduino for ESP8266&ESP32适用库ESPAsyncWebServer:事件处理绑定》
《Arduino for ESP8266&ESP32适用库ESPAsyncWebServer:请求与响应》
《Arduino for ESP8266&ESP32适用库ESPAsyncWebServer:静态文件和模板引擎》
《Arduino for ESP8266&ESP32适用库ESPAsyncWebServer:WebSocket和EventSource》