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_send
的 documentation:
... 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;
}
我用的是带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_send
的 documentation:
... 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;
}