node.js - 使用 ESP-IDF 从 node.js 服务器发送图像到 ESP32
问题描述
我正在尝试使用 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();
在客户端,我确保缓冲区分配的空间大于图像文件的大小。当我打印出客户端缓冲区中存储的内容时,我看到了绝对垃圾。服务器端发送如下所示的缓冲流:
<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 文件的签名一致。
I've set the client side buffer at 10000 (content length is 9037 bytes). Still, I receive the hex string in two chunks. In my client side code, function http_perform_as_stream_reader
calls esp_http_client_fetch_headers
from the ESP component esp_http_client
, which in turn calls the lwip_rec_tcp
function from lwip/src/api/sockets.c
. Since I've set the buffer capacity to 10000 (a rather large amount), esp_http_client_fetch_headers
fetches a rather large chunk of the hex string along with the headers. Then when the http_perform_as_stream_reader
function calls the esp_http_client_read
function, it again calls lwip_rec_tcp
which now runs a do ... while loop until it retrieves the remaining server side data. Since lwip_rec_tcp
stores all the data in the buffer, which has low storage capacity (certainly not up to the 10000 that I set for it in the code), the first chunk gets overwritten by the final chunk of data. So how do I ensure that the client->response->buffer
pointer captures all the chunks of data without modifying lwip_rec_tcp
to include uploading data to the spiffs file inside the do ... while loop?
解决方案
Application should not assume that esp_http_client_read reads same number of bytes specified in the length argument. Instead, application should check the return value of this API which indicates number of bytes read by corresponding call. If the return value is zero, it indicates that complete data is read.
To work correctly, application should call esp_http_client_read in a while loop with a check for return value of esp_http_client_read.
You can also use esp_http_client_read_response, which is a helper API for esp_http_client_read to internally handle the while loop and read complete data in one go.
Please refer http_native_request example which uses esp_http_client_read_response()
API.
推荐阅读
- laravel - 调用未定义的方法 Maatwebsite\Excel\Facades\Excel::download()
- jupyter-notebook - 获取当前 Jupyter 服务器 IPython 连接到的基本 URL
- javascript - 我想生成一个循环,该循环将在数组中的每个等级中“添加”加分,然后将新分数与给定的标准进行比较
- cocoa - Big Sur 统一工具栏——如何... NSWindow/NSToolbar
- multithreading - Selenium 使用多线程浏览器并行执行带来了前沿问题
- c# - 我无法调试 .NET Core 源代码
- azure - 通过 Azure 数据工厂处理 SSAS
- rust - 如何在 Rust 中内联 ASM 字节语句?
- react-native - flex 在 react-native 的 ScrollView 中不起作用
- string - 如何在python中找到两个不重叠的字符串