太好了!接下来我们继续深入 Node-API 的高级应用,我会带你实现 文件哈希计算 、真实进度条 和 图像滤镜,这些都是实际开发中非常有用的功能。
1. 文件哈希计算器(异步操作实战)
目标:用 C++ 计算文件的 SHA-256 哈希值,通过 Promise 返回给 JS。
**修改 hello.cc
**
ini
#include <fstream>
#include <openssl/sha.h> // 需要安装 OpenSSL
// 1. 异步任务数据结构
struct HashData {
napi_async_work work;
napi_deferred deferred;
std::string filePath;
std::string hashResult;
};
// 2. 计算哈希的函数(在子线程运行)
void ComputeHash(napi_env env, void* data) {
HashData* hash_data = static_cast<HashData*>(data);
std::ifstream file(hash_data->filePath, std::ios::binary);
if (!file) {
hash_data->hashResult = "FILE_OPEN_ERROR";
return;
}
SHA256_CTX sha256;
SHA256_Init(&sha256);
char buffer[1024];
while (file.read(buffer, sizeof(buffer))) {
SHA256_Update(&sha256, buffer, file.gcount());
}
SHA256_Update(&sha256, buffer, file.gcount());
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_Final(hash, &sha256);
char hexHash[65];
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
sprintf(hexHash + (i * 2), "%02x", hash[i]);
}
hexHash[64] = '\0';
hash_data->hashResult = hexHash;
}
// 3. 完成回调
void HashComplete(napi_env env, napi_status status, void* data) {
HashData* hash_data = static_cast<HashData*>(data);
napi_value result;
if (hash_data->hashResult == "FILE_OPEN_ERROR") {
napi_throw_error(env, nullptr, "无法打开文件");
} else {
napi_create_string_utf8(env, hash_data->hashResult.c_str(), NAPI_AUTO_LENGTH, &result);
napi_resolve_deferred(env, hash_data->deferred, result);
}
napi_delete_async_work(env, hash_data->work);
delete hash_data;
}
// 4. JS 调用的入口函数
napi_value CalculateFileHash(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
char filePath[1024];
size_t filePathLength;
napi_get_value_string_utf8(env, args[0], filePath, sizeof(filePath), &filePathLength);
napi_value promise;
HashData* hash_data = new HashData();
hash_data->filePath = filePath;
napi_create_promise(env, &hash_data->deferred, &promise);
napi_value resource_name;
napi_create_string_utf8(env, "CalculateFileHash", NAPI_AUTO_LENGTH, &resource_name);
napi_create_async_work(env, nullptr, resource_name,
ComputeHash, HashComplete,
hash_data, &hash_data->work);
napi_queue_async_work(env, hash_data->work);
return promise;
}
// 5. 在 Init 里注册
napi_value Init(napi_env env, napi_value exports) {
napi_value fn;
napi_create_function(env, nullptr, 0, CalculateFileHash, nullptr, &fn);
napi_set_named_property(env, exports, "calculateFileHash", fn);
return exports;
}
修改 binding.gyp
(添加 OpenSSL 支持)
php
{
"targets": [
{
"target_name": "hello_napi",
"sources": ["hello.cc"],
"libraries": ["-lcrypto"],
"include_dirs": ["<!@(node -p "require('node-addon-api').include")"],
"dependencies": ["<!(node -p "require('node-addon-api').gyp")"]
}
]
}
**修改 index.js
**
javascript
const addon = require('./build/Release/hello_napi.node');
(async () => {
try {
const hash = await addon.calculateFileHash('test.txt');
console.log('文件哈希:', hash);
} catch (err) {
console.error('出错:', err);
}
})();
运行
bash
echo "Hello Node-API" > test.txt # 创建测试文件
node-gyp rebuild
node index.js
输出示例:
makefile
文件哈希: a591a6d40bf420404a011733...
2. 真实进度条(线程安全实战)
目标:模拟文件下载,每秒更新进度到 JS。
**修改 hello.cc
**
ini
// 1. 进度数据
struct DownloadData {
napi_threadsafe_function tsfn;
int progress;
bool isDownloading;
};
// 2. 模拟下载的函数
void DownloadThread(DownloadData* data) {
while (data->progress < 100 && data->isDownloading) {
data->progress += 10;
// 调用 JS 回调
napi_call_threadsafe_function(
data->tsfn,
nullptr,
napi_tsfn_nonblocking
);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
napi_release_threadsafe_function(data->tsfn, napi_tsfn_release);
delete data;
}
// 3. JS 调用的入口函数
napi_value StartDownload(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
DownloadData* data = new DownloadData();
data->progress = 0;
data->isDownloading = true;
napi_create_threadsafe_function(
env,
args[0],
nullptr,
napi_create_string_utf8(env, "DownloadProgress", NAPI_AUTO_LENGTH),
0, 1, nullptr,
[](napi_env env, void* finalize_data, void* hint) {},
&data->tsfn
);
std::thread(DownloadThread, data).detach();
// 返回一个可以取消下载的函数
napi_value cancelFn;
napi_create_function(env, nullptr, 0, [](napi_env env, napi_callback_info info) {
DownloadData* data = static_cast<DownloadData*>(info->data);
data->isDownloading = false;
return nullptr;
}, data, &cancelFn);
return cancelFn;
}
// 4. 在 Init 里注册
napi_value Init(napi_env env, napi_value exports) {
napi_value fn;
napi_create_function(env, nullptr, 0, StartDownload, nullptr, &fn);
napi_set_named_property(env, exports, "startDownload", fn);
return exports;
}
**修改 index.js
**
javascript
const addon = require('./build/Release/hello_napi.node');
const cancelDownload = addon.startDownload((progress) => {
console.log(`下载进度: ${progress}%`);
if (progress === 100) console.log('下载完成!');
});
// 5秒后取消下载
setTimeout(() => {
cancelDownload();
console.log('下载已取消');
}, 5000);
运行
node-gyp rebuild
node index.js
输出示例:
erlang
下载进度: 10%
下载进度: 20%
...
下载已取消
3. 图像反色滤镜(Buffer 实战)
目标:将图片的 RGB 值反色(255 - 当前值)。
**修改 hello.cc
**
ini
napi_value InvertImage(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 获取 Uint8Array 的 Buffer
napi_typedarray_type type;
size_t length;
void* data;
napi_value buffer;
size_t offset;
napi_get_typedarray_info(env, args[0], &type, &length, &data, &buffer, &offset);
if (type != napi_uint8_array) {
napi_throw_error(env, nullptr, "需要 Uint8Array");
return nullptr;
}
// 反色处理
uint8_t* pixels = static_cast<uint8_t*>(data);
for (size_t i = 0; i < length; i++) {
pixels[i] = 255 - pixels[i];
}
return args[0]; // 返回修改后的 Buffer
}
// 在 Init 里注册
napi_value Init(napi_env env, napi_value exports) {
napi_value fn;
napi_create_function(env, nullptr, 0, InvertImage, nullptr, &fn);
napi_set_named_property(env, exports, "invertImage", fn);
return exports;
}
**修改 index.js
**
ini
const fs = require('fs');
const addon = require('./build/Release/hello_napi.node');
// 读取图片文件
const imageBuffer = fs.readFileSync('input.jpg');
const pixels = new Uint8Array(imageBuffer);
// 反色处理
addon.invertImage(pixels);
// 保存结果
fs.writeFileSync('output.jpg', imageBuffer);
console.log('图像反色完成!');
运行
node-gyp rebuild
node index.js
你会看到生成的 output.jpg
是输入图片的反色版本。
总结
项目 | 技术要点 | 应用场景 |
---|---|---|
文件哈希计算 | 异步操作 + OpenSSL | 文件校验、安全检测 |
下载进度条 | 线程安全函数 + 取消控制 | 大文件下载、实时监控 |
图像滤镜 | 直接操作 Buffer | 图像处理、计算机视觉 |
下一步挑战
- 尝试用 Node-API 实现一个 视频帧处理器(提取关键帧)。
- 结合 WebSocket 实现 实时数据传输(如股票行情)。
- 开发一个 加密/解密模块(AES + Node-API)。
如果有任何问题,欢迎继续提问! 😊