esp32 http_server html 如何显示来自 sd 卡的图像

esp32 http_server html how to show image from sd card

我用的是带sdcard接口的esp32cam模块。我能够保存在 SD 卡中的图像。此图片在 Windows PC 中打开。接下来,我所做的是添加 http_server 并在 html 页面中我想从 sdcard 访问图像并在 img 标签的 src 属性中使用。但是 html 不显示图像并且在 teraterm 日志中它说找不到文件。

 httpd_uri: httpd_uri: URI '/sdcard/fnb1.jpg' not found

服务于html请求的代码如下:

1   static esp_err_t hello_get_handler(httpd_req_t *req) {  
2   strcpy(ret_homepage,"<!DOCTYPE html><html><head><title>SwitchControl</title>"); 
3   strcat(ret_homepage, "</head><body>");  
4   strcat(ret_homepage, "<div>");  
5   strcat(ret_homepage, "Picture1:");  
6   strcat(ret_homepage,"<img src=\"/sdcard/fnb1.jpg\" width=\"500\" height=\"600\">"); 
7   strcat(ret_homepage, "</div>"); 
8       
9   strcat(ret_homepage, "</body>");    
10  strcat(ret_homepage, "</html>");    
11      
12  /* Set some custom headers */   
13  httpd_resp_set_hdr(req, "Connection", "close"); 
14  httpd_resp_set_hdr(req, "Cache-Control", "no-cache");   
15      
16  /* Send response with custom headers and body set as the    
17   * string passed in user context*/  
18  const char *resp_str = (const char*) ret_homepage;  
19      
20  httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);  
21      
22  ESP_LOGI(TAG, "Response sent for home page request.Time:%s",esp_log_system_timestamp());    
23      
24  return ESP_OK;  
25  }

这里是第 6 行

strcat(ret_homepage,"<img src=\"/sdcard/fnb1.jpg\" width=\"500\" height=\"600\">");

http_server 似乎无法访问 mount_point“/sdcard”,因此无法访问 fbn1.jpg。

SD卡的挂载我是这样完成的。此安装工作正常,因为写入的文件可从 windows PC 读取。接下来的事情是为什么上面的行不读呢?可能它需要注册到虚拟文件系统 (VFS)!如果是这样,那么在“esp_vfs_fat_sdspi_mount”之后,第 37 行我必须执行“esp_vfs_fat_register”才能使其工作吗?请问有谁能给点建议吗?

#define MOUNT_POINT "/sdcard"
....
1   void mount_sdcard(){    
2       esp_err_t ret;
3       esp_vfs_fat_sdmmc_mount_config_t mount_config = {
4       #ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
5               .format_if_mount_failed = true,
6       #else
7               .format_if_mount_failed = false,
8       #endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
9               .max_files = 5, .allocation_unit_size = 16 * 1024
10          };
11          sdmmc_card_t* card;
12          const char mount_point[] = MOUNT_POINT;
13          ESP_LOGI(TAG, "Initializing SD card");
14      #ifndef USE_SPI_MODE
15          ESP_LOGI(TAG, "Using SDMMC peripheral");
16          sdmmc_host_t host = SDMMC_HOST_DEFAULT();
17          sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
18      
19          // GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
20          gpio_set_pull_mode(15, GPIO_PULLUP_ONLY);   // CMD, needed in 4- and 1- line modes
21          //... omitted for brevity
22          gpio_set_pull_mode(13, GPIO_PULLUP_ONLY);   // D3, needed in 4- and 1-line modes
23          ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
24      #else
25          ESP_LOGI(TAG, "Using SPI peripheral");
26      
27          sdmmc_host_t host = SDSPI_HOST_DEFAULT();
28          spi_bus_config_t bus_cfg = {  .mosi_io_num = PIN_NUM_MOSI,.miso_io_num = PIN_NUM_MISO,.sclk_io_num = PIN_NUM_CLK,.quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = 4000,  };
29          ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN);
30          if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize bus.");return;   }
31      
32          // This initializes the slot without card detect (CD) and write protect (WP) signals.
33          // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
34          sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
35          slot_config.gpio_cs = PIN_NUM_CS;slot_config.host_id = host.slot;
36      
37          ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
38      #endif //USE_SPI_MODE
39      
40          if (ret != ESP_OK) {   //...
41              return;
42          }
43      
44          // Card has been initialized, print its properties
45          sdmmc_card_print_info(stdout, card);
46  }

在做任何事情之前,你需要了解你在做什么。

您正在使用 ESP32 HTTP 服务器 API。你检查过它的 documentation 了吗?它甚至有一个示例程序。

现在,您已经为某个 URI 注册了一个处理程序。它可能看起来像这样? :)(或者您可能更改了 uri。)

static const httpd_uri_t hello = {
    .uri       = "/hello",
    .method    = HTTP_GET,
    .handler   = hello_get_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx  = NULL 
};

...

httpd_register_uri_handler(server, &hello);

这意味着,如果您的服务器收到一个 HTTP GET request 的 /hello,您的程序将调用 hello_get_handler(),然后用您在处理程序中编写的 HTML 文档进行响应。

HTML 文档就在那里。浏览器只接收你发送的文本,仅此而已。在浏览器中查看源代码。换句话说,src=\"/sdcard/fnb1.jpg\" 此时(或永远)不会被图像替换。

<img>tag/element只是一个指令,让浏览器在那个地方显示'src'指向的资源,给定的参数。 'src' 甚至可以是另一台服务器上的资源,例如:

<img src=\"http://cdn.sstatic.net/Img/teams/teams-illo-free-sidebar-promo.svg\">

(尝试使用一些已知的图像地址。)一个例外:图像也可以是整个图像文件的 Base64 编码字符串。但是,它不能是实际文件本身,即作为二进制 blob 的 jpeg。如果您真的想将图像嵌入到文档中,您可以使用 Base64 approach,但这不是您在此处尝试执行的操作。

所以重申一下,图像本身并没有传输到这里。只有描述图像元素的 HTML 代码。纯文本。

浏览器将为图像资源本身发出单独的 HTTP GET 请求。您定义了一个相对 URI,因此浏览器尝试从 <your_server>/sdcard/fnb1.jpg.

获取它

您的代码没有能够响应 GET 的处理程序,因此未显示图像。

所以再次假设您的 ESP32 获得 IP 192.168.1.42,这基本上是发生的事情:

所以您需要做的是实现并注册另一个也可以为图像提供服务的句柄。此时它可以是专门处理一个图像的处理程序。只需编写一些 URI 并将其嵌入到您的 <img>。 URI 当然可以保留为 /sdcard/fnb1.jpg - 没关系。只需了解这是您的 HTTP 服务器中的 URI。它与 SD 卡中图像的文件系统路径 完全没有关系

响应 HTTP 200 OK 响应,至少 header 'content-type: image/jpeg'。根据 httpd_resp_senddocumentation

... If no status code and content-type were set, by default this will send 200 OK status code and content type as text/html. ... httpd_resp_set_type() - for setting the Content Type ...

所以你只需要设置content type("image/jpeg")。

再一次:您需要定义一个响应处理程序来处理对图像的 GET 请求。至少它可以是一个特定图像的硬编码 URI。在处理程序中,您需要使用 httpd_resp_send 从 SD 卡(或任何地方)发送文件并将 content-type header 设置为 "image/jpeg"httpd_resp_set_type .所以像:

static const httpd_uri_t img_example = {
    .uri       = "/img/example.jpg",
    .method    = HTTP_GET,
    .handler   = img_example_get_handler,
    .user_ctx  = NULL 
};

static esp_err_t img_example_get_handler(httpd_req_t *req)
{
  // 1. get a pointer to your image file
  // 2. httpd_resp_set_type to set the correct content type header
  //    esp_err_t httpd_resp_set_type(httpd_req_t *r, const char *type)
  //      Parameters
  //        [in] r: The request being responded to
  //        [in] type: The Content Type of the response
  // 3. httpd_resp_send to send the response, with the image file as buf
  //    esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, ssize_t buf_len)
  //      Parameters
  //        [in] r: The request being responded to
  //        [in] buf: Buffer from where the content is to be fetched
  //        [in] buf_len: Length of the buffer, HTTPD_RESP_USE_STRLEN to use strlen()

  return ESP_OK;
}