ESP32 Arduino: Serving file as attachment

In this tutorial we will check how to serve a file as a downloadable attachment, using the ESP32, the Arduino core and the async HTTP web server library. The tests of this tutorial were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.

Introduction

In this tutorial we will check how to serve a file as a downloadable
attachment, using the ESP32, the Arduino core and the async HTTP web server library.

We will be serving a HTML file to show that the browser will download it instead of displaying its rendered content.

We will be serving the file from the SPIFFS file system of the ESP32. To upload a file to the SPIFFS file system, we will make use of an Arduino IDE plugin which can be found here. For an introductory guide on how to use it, please check this previous tutorial.

In short, in order to upload a file using the mentioned plugin, you should navigate to the Arduino sketch folder where you will write your code and create a folder named data there.

Inside the folder, you need to place the files to upload. In our case, it will be a HTML file called “file.html” with the following content:

<p>Awesome file content!</p>

After creating the “file.html” inside the data folder and writing the previous content inside, simply go back to the Arduino IDE and under the Tools menu click “ESP32 Sketch Data Upload” The uploading procedure should begin.

After the uploading procedure finishes, the file should be available on the SPIFFS file system, to be served from our application.

The tests of this tutorial were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.

The code

We will start our code by including the libraries we need. We will use the following 3:

  • WiFi.h: Allows to connect the ESP32 to a WiFi network;
  • ESPAsyncWebServer.h: Allows to setup an asynchronous HTTP web server on the ESP32;
  • SPIFFS.h: allows to interact with the SPIFFS file system of the ESP32.

In order to be able to connect the ESP32 to a WiFi network, we will need its credentials. We will need both the network name and the password. We will declare them as global variables, so they can be easily changed.

We will also need an object of class AsyncWebServer, which will allow us to setup the server routes and start its operation.

#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
 
const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";
 
AsyncWebServer server(80);

Moving on to the Arduino setup function, we will first open a serial connection. Then, we will initialize the SPIFFS file system and, after that, connect the ESP32 to the WiFi network.

Note that once the WiFi connection is established, we will print the local IP assigned to the device. This IP will be needed by the HTTP client, so it knows where to contact the server.

Serial.begin(115200);
 
if(!SPIFFS.begin()){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
}
 
WiFi.begin(ssid, password);
 
while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
}
 
Serial.println(WiFi.localIP());

After the mentioned initialization procedures, we will setup the server route that will be responsible for serving the file. We will call this route “/download” and it will only listen to HTTP GET requests.

In the implementation of the route handling function, we will use one of the overloaded versions of the send method of the AsyncWebServerRequest class. Recall from previous tutorials that the route handling functions will receive a pointer to an object of this class, which can be used to return back the answer to the client.

In this case, we will use the version of the send method that has the following 4 arguments:

  • An object of class FS, which represents a File System. We will be passing the SPIFFS extern variable, which inherits from this class and will be used under the hood to access the file;
  • A string with the path of the file in the file system. Our file should be on “/file.html“;
  • The content type, which should be “text/html” since we are serving a HTML file;
  • A Boolean value indicating if the file should be served as an attachment to be downloaded and saved locally (true) or as regular content to be interpreted and displayed in the browser (false). For our test, we should pass true.

The practical effect of setting the mentioned flag to true will consist on adding the Content-Disposition header to the server response, with the value “attachment“, which will tell the browser to download the content as an attachment.

Note that this fourth parameter defaults to false when not specified, which means the default behavior corresponds to indicate to the browser that the content should be interpreted and displayed.

This is why, in previous tutorials where we served files and omitted this flag, they were interpreted and displayed by the browser.

server.on("/download", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/file.html", "text/html", true);
});

We will declare an additional route where we will serve the file normally. In this case, the browser should render the HTML and display it.

This route will be called “/interpret” and its handling function implementation will be exactly the same as before, except that we will now pass false as fourth parameter of the send method.

server.on("/interpret", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/file.html", "text/html", false);
});

To finalize, we need to call the begin method on the server object. This method takes no arguments and it will ensure the server will start listening to incoming requests.

server.begin();

The final code can be seen below.

#include "WiFi.h"
#include "SPIFFS.h"
#include "ESPAsyncWebServer.h"

 
const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPass";
 
AsyncWebServer server(80);
 
void setup(){
  Serial.begin(115200);
 
  if(!SPIFFS.begin()){
        Serial.println("An Error has occurred while mounting SPIFFS");
        return;
  }
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
 
  Serial.println(WiFi.localIP());
 
  server.on("/download", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/file.html", "text/html", true);
  });
  
  server.on("/interpret", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/file.html", "text/html", false);
  });
 
  server.begin();
}
 
void loop(){}

Testing the code

To test the code, simply compile it and upload it to your ESP32 device, using the Arduino IDE. Once the procedure finishes, open the IDE Serial Monitor.

After the ESP32 connects to the WiFi network, an IP address should get printed. Copy that address.

Then, open a web browser of your choice and type the following, changing #yourDeviceIp# by the IP you have just copied:

http://#yourDeviceIp#/download

You should get an output similar to figure 1. As can be seen, after accessing the URL, the file will be served by the ESP32 and the browser will download it. The answer to the request contains the Content-Disposition header, as expected.

Serving a HTML file as attachment, using the Arduino core and the ESP32.
Figure 1 – Serving a HTML file as attachment.

To test the other route where the content will be interpreted by the browser, simply access the following URL:

http://#yourDeviceIp#/interpret

You should get an output like figure 2, which now shows the content of the HTML file being rendered by the browser.

Serving HTML file to be interpreted by the browser, using the ESP32 and the Arduino core
Figure 2 – Serving the file to be interpreted by the browser.

Related Posts

Leave a Reply

%d bloggers like this: