文章目录

  • 目的
  • 静态文件服务
  • 功能说明
  • 使用演示
  • SPIFFSEditor
  • 模板引擎
  • 总结


目的

对于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的文件:

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>ServeStatic Test</title>
    <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 <WiFi.h>
#include <SPIFFS.h>
#include <ESPAsyncWebServer.h> //引入相应库

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 扫描 服务器 IP esp32 server_物联网


上面使用了名为 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

SPIFFSEditor

ESPAsyncWebServer库中有一个SPIFFSEditor功能,可以通过网页管理和编辑文件系统中的文件,这里做个简单的使用演示:

#include <WiFi.h>
#include <SPIFFS.h>
#include <ESPAsyncWebServer.h> //引入相应库
#include <SPIFFSEditor.h> //引入相应库

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(){}

esp32 扫描 服务器 IP esp32 server_esp8266_02


上面就是SPIFFSEditor的演示了,需要注意的是SPIFFSEditor的网页中用到了外部的文件,需要能连上互联网才能正确加载。

模板引擎

ESPAsyncWebServer中的模板引擎目前只支持一个功能,实际值替换占位符。该模板引擎使用 % 来标识占位符,先看下面演示:

#include <WiFi.h>
#include <ESPAsyncWebServer.h> //引入相应库

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(){}

esp32 扫描 服务器 IP esp32 server_esp8266_03


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