使用 ESP-IDF 将图像从 node.js 服务器发送到 ESP32

Sending image from node.js server to ESP32 using ESP-IDF

我正在尝试使用 WIFI 和 ESP-IDF 将图像从 node.js 服务器发送到 ESP32 芯片。我相信这种传输是通过 TCP/IP 连接进行的。我可以使用 http get 请求或下面显示的 http_perform_as_stream_reader 函数发送常规文本数据。但是当涉及到从 node.js 服务器传输图像时,我无法这样做。我似乎在客户端收到垃圾。最终目标是将图像保存到 ESP32 上的 spiffs 文件中。这是我的代码:

服务器端:

const {imageprocess, convertToBase64} = require('../canvas');
const fs = require('fs');
const express = require('express');
const router = express.Router();
const path = require('path');

const imageFile = path.resolve(__dirname, "../images/file31.png")

router.get('/', funcAllStream)

module.exports = router;


function funcAllStream(req, res, next){
        newStream(res, imageFile)
}

function newStream(res, imageFile){
  var readStream = fs.createReadStream(imageFile);
  readStream.on('data', chunk => {
    res.send(chunk.toString('hex'));
  })
}

客户端(C++):

static void http_perform_as_stream_reader(void)
{
    char *buffer = malloc(MAX_HTTP_RECV_BUFFER + 1);
    esp_http_client_config_t config = {
        .url = "http://192.168.1.155:8085/api?file=image1.png"
    };
    esp_http_client_handle_t client = esp_http_client_init(&config); //calls esp_http_client_init from standard ESP32 component esp_http_client
    esp_err_t err;
    if ((err = esp_http_client_open(client, 0)) != ESP_OK) {
        return;
    }
    int content_length =  esp_http_client_fetch_headers(client); //calls esp_http_client_fetch_headers from standard ESP32 component esp_http_client
    int total_read_len = 0, read_len;
    if (total_read_len < content_length && content_length <= MAX_HTTP_RECV_BUFFER) {//MAX_HTTP_RECV_BUFFER defined to be larger than image1.png file on server side
        read_len = esp_http_client_read(client, buffer, content_length); //calls esp_http_client_read from standard ESP32 component esp_http_client
        if (read_len <= 0) {
            ESP_LOGE(TAG, "Error read data");
        }
        buffer[read_len] = 0;
    }
    esp_http_client_close(client);
    esp_http_client_cleanup(client);
    free(buffer);
}

http_perform_as_stream_reader();

在客户端,我已确保分配的缓冲区 space 大于图像文件的大小。当我打印出存储在客户端缓冲区中的内容时,我看到了绝对的垃圾。服务器端发送如下所示的缓冲流:

<Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 64 00 00 00 64 08 06 00 00 00 70 e2 95 54 00 00 00 06 62 4b 47 44 00 ff 00 ff 00 ff a0 bd a7 ...>

这是客户端缓冲区在接收数据之前的样子:

���?Lv�~sN\
L��8J��s-�z���a�{�;�����\���~�Y���Di�2���]��|^,�x��N�݁�����`2g����n�w��b�Y�^���a���&��wtD�>n@�PQT�(�.z��(9,�?İ�

这是接收数据后客户端缓冲区的样子:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/octet-stream
Content-Length: 4517
ETag: W/"11a5-IhqwFPYLgC+NRfikTwS2exLtCWQ"
Date: Mon, 19 Apr 2021 16:15:48 GMT
Connection: keep-alive

�PNG

那么如何确保客户端缓冲区实际从服务器端接收到正确格式的正确数据呢?最终,我想在客户端将数据存储在一个spiffs文件中,并打开该文件以显示从node.js服务器传来的图像。

更新: 在服务器端将数据转换为十六进制格式后,我可以确认客户端收到了正确的十六进制字符串。这是我在服务器端和客户端看到的:

89504e470d0a1a0a0000000d4948445200000064000000 ... 0fcc3c0ff03b8c85cc0643044ae0000000049454e44ae426082

起止签名与png文件一致

我已将客户端缓冲区设置为 10000(内容长度为 9037 字节)。尽管如此,我还是分两部分收到了十六进制字符串。在我的客户端代码中,函数 http_perform_as_stream_reader 从 ESP 组件 esp_http_client 调用 esp_http_client_fetch_headers,后者又从 lwip/src/api/sockets.c 调用 lwip_rec_tcp 函数。由于我已将缓冲区容量设置为 10000(相当大的数量),esp_http_client_fetch_headers 会连同 headers 一起获取相当大的十六进制字符串块。然后当 http_perform_as_stream_reader 函数调用 esp_http_client_read 函数时,它再次调用 lwip_rec_tcp 现在运行一个 do ... while 循环,直到它检索剩余的服务器端数据。由于 lwip_rec_tcp 将所有数据存储在缓冲区中,缓冲区的存储容量较低(当然达不到我在代码中为其设置的 10000),因此第一个块会被最后一个数据块覆盖。那么如何确保 client->response->buffer 指针捕获所有数据块而不修改 lwip_rec_tcp 以包括将数据上传到 do ... while 循环内的 spiffs 文件?

应用程序不应假定 esp_http_client_read 读取长度参数中指定的相同字节数。相反,应用程序应检查此 API 的 return 值,该值指示相应调用读取的字节数。如果return值为零,表示读取完整数据。

要正常工作,应用程序应在 while 循环中调用 esp_http_client_read,并检查 esp_http_client_read 的 return 值。

您还可以使用 esp_http_client_read_response,它是 esp_http_client_read 的助手 API,可以在内部处理 while 循环并一次性读取完整数据。

请参考 http_native_request 使用 esp_http_client_read_response() API 的示例。