Search code examples
filewebsocketesp32micropython

Micropython Webserver: Serve large textfiles without memory allocation failure


My project includes a webserver using an ESP32 and micropython v1.12

Background:

I want to create config page which allows me to enter WiFi credentials for connection my ESP to my home network. I’m doing this running a webserver running on my ESP32 at start. For this I planned using Bootstrap and their CSS Style sheet.

Basically I'm starting the server using:

server_socket = socket.socket()
server_socket.bind(addr)
server_socket.listen(1)
...

If a client connects to my webserver, I'm parsing the URL and calling a handle-method. This is also the case for my css-files.

# Get the URL
url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/")

# Math the url
if url == "":
  handle_root(client)
elif url == "bootstrap.min.css":
  handle_css(client, request, path='bootstrap.min.css')
else:
  handle_not_found(client, url)

I'm responding using the following lines of code:

def handle_css(client, request, path):
  wlan_sta.active(True)
  path = './config_page/' + path # The path to the css
  f = open(path, 'r') # Open the file
  client.send(f.read()) # Read the file and send it
  client.close()
  f.close()

The file bootstrap.min.css has around 141kB. I'm running out of memory reading this file and sending it using the socket:

MemoryError: memory allocation failed, allocating 84992 bytes

Is there a way to serve "big" files like the .css file? The config page depends on some these files.


Solution

  • Sure. The problem here is probably this line client.send(f.read()) which reads the entire file to memory and sends it to the client. Instead of reading the entire file at once, try reading it in 1KB chunks and sending those to the client.

    f = open(path, 'r') # Open the file
    while True:
        chunk = f.read(1024) # Read the next 1KB chunk
        if not chunk:
            break
        client.send(chunk) # Send the next 1KB chunk
    client.close()
    f.close()