http_server filesystem handler example?

phatpaul
Posts: 110
Joined: Fri Aug 24, 2018 1:14 pm

http_server filesystem handler example?

Postby phatpaul » Wed Aug 29, 2018 9:59 pm

I see there is a new http_server module now in the ESP-IDF.

It looks interesting. What are it's features/benefits over some of the other servers available? (I've been using libesphttpd. Also I see nghttp module in the idf.)

The documentation is here: https://docs.espressif.com/projects/esp ... erver.html

But I don't see an example of serving static files from the filesystem. Does anyone have an example project showing how to do that?
Thanks.

malaugh
Posts: 4
Joined: Mon Aug 20, 2018 8:17 pm

Re: http_server filesystem handler example?

Postby malaugh » Thu Aug 30, 2018 3:28 pm

I am assuming you are talking about serving web pages. I have something similar it serves up web pages stored in program memory, added as header files. Her are som code snippets

Code: Select all

// include your web page a header file
#include "web_index.h"

// code to pass character pointer and size of web page
typedef struct {
    const unsigned char *resp;
    size_t              *resp_len;
} WEBKIT_RESPONSE_ARGS;

WEBKIT_RESPONSE_ARGS webkit_index_args = { index_htm_gz, &index_htm_gz_len };

// function prototype
esp_err_t webkit_request_handler(httpd_req_t *req);

// structure for registering callback for browser index request.
httpd_uri_t webkit_index_req =    { .uri = "/", .method = HTTP_GET, .handler = webkit_request_handler, .user_ctx  = (void *)&webkit_index_args };

// Function to send back web page
esp_err_t webkit_request_handler(httpd_req_t *req)
{
    WEBKIT_RESPONSE_ARGS* args = (WEBKIT_RESPONSE_ARGS *)(req->user_ctx);
    
    httpd_resp_set_type(req, HTTPD_TYPE_TEXT);    
    httpd_resp_set_hdr(req, "Content-Encoding", "gzip");    
    httpd_resp_send(req, (const char*)args->resp, *(args->resp_len));
    return ESP_OK;
}

// function to start web server and register callback for index page
httpd_handle_t start_webserver(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    
    config.max_uri_handlers = 20;

    if (httpd_start(&server, &config) == ESP_OK) {
        httpd_register_uri_handler(server, &webkit_index_req);
        return server;
    }
    return NULL;
}
You also need a shell script to convert the html files (or any other type of file) to a header to be included in the project. Note that the files are gzipped so they use less program memory.

Code: Select all

#!/bin/bash
gzip -k index.htm
xxd  -i index.htm.gz         ../main/web_index.h
rm *.gz

ESP_Anurag
Posts: 19
Joined: Fri Aug 31, 2018 5:37 am

Re: http_server filesystem handler example?

Postby ESP_Anurag » Fri Aug 31, 2018 11:26 am

Presently there is no out of the box support for file sharing, though it is something desirable and will be made available in future. Till then, implementing a simple file server should not be difficult. Take for this as an example :

Code: Select all

/* HTTP GET handler for downloading files */
esp_err_t file_get_handler(httpd_req_t *req)
{
    /* Assuming that the sdcard has been initialized (formatted to FAT) and loaded with some files. 
     * Refer to sd_card_example to to know how to initialize the sd card */
    const char *filepath_prefix = "/sdcard/";
    char *filename = NULL;
    size_t filename_len = httpd_req_get_url_query_len(req);

    if (filename_len == 0) {
        const char * resp_str = "Please specify a filename. eg. file?somefile.txt";
        httpd_resp_send(req, resp_str, strlen(resp_str));
        return ESP_OK;
    }
    filename = malloc(strlen(filepath_prefix) + filename_len + 1); // extra 1 byte for null termination
    strncpy(filename, filepath_prefix, strlen(filepath_prefix));

    // Get null terminated filename
    httpd_req_get_url_query_str(req, filename + strlen(filepath_prefix), filename_len + 1);
    ESP_LOGI(TAG, "Reading file : %s", filename + strlen(filepath_prefix));

    FILE *f = fopen(filename, "r");
    free(filename);
    if (f == NULL) {
        const char * resp_str = "File doesn't exist";
        httpd_resp_send(req, resp_str, strlen(resp_str));
        return ESP_OK;
    }

    /* Read file in chunks (relaxes any constraint due to large file sizes)
     * and send HTTP response in chunked encoding */
    char   chunk[1024];
    size_t chunksize;
    do {
        chunksize = fread(chunk, 1, sizeof(chunk), f);
        if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
            fclose(f);
            return ESP_FAIL;
        }
    } while (chunksize != 0);

    httpd_resp_send_chunk(req, NULL, 0);
    fclose(f);
    return ESP_OK;
}

httpd_handle_t start_file_server(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    httpd_uri_t file_serve = {
        .uri       = "/file",
        .method    = HTTP_GET,
        .handler   = file_get_handler,
        .user_ctx  = NULL
    };

    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: %d", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK) {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &file_serve);
        return server;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return NULL;
}
So in order to download a file one can run something like this :

Code: Select all

curl http://10.0.0.152:80/file?music/song.mp3 > song.mp3
Assuming "10.0.0.152" is the IP assigned to ESP32, and there is a file "music/song.mp3" in the FAT formatted sdcard.

Who is online

Users browsing this forum: noweare and 134 guests