Node-API 学习三

太好了!接下来我们继续深入 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 图像处理、计算机视觉

下一步挑战

  1. 尝试用 Node-API 实现一个 视频帧处理器(提取关键帧)。
  2. 结合 WebSocket 实现 实时数据传输(如股票行情)。
  3. 开发一个 加密/解密模块(AES + Node-API)。

如果有任何问题,欢迎继续提问! 😊

相关推荐
一枚前端小能手9 小时前
🚀 Webpack打包慢到怀疑人生?这6个配置让你的构建速度起飞
前端·javascript·webpack
前端缘梦9 小时前
深入浅出 Vue 的 Diff 算法:最小化 DOM 操作的魔法
前端·vue.js·面试
月伤599 小时前
Element Plus 表格表单校验功能详解
前端·javascript·vue.js
BUG收容所所长9 小时前
JavaScript并发控制:如何优雅地管理异步任务执行?
前端·javascript·面试
非优秀程序员9 小时前
46个Nano-banana 精选提示词,持续更新中
前端
Mintopia10 小时前
Next 全栈数据缓存(Redis)从入门到“上瘾”:让你的应用快到飞起 🚀
前端·javascript·next.js
chxii10 小时前
7.5el-tree 组件详解
前端·javascript·vue.js
Mintopia10 小时前
每个国家的核安全是怎么保证的,都不怕在自己的领土爆炸吗?
前端·后端·面试
BUG收容所所长10 小时前
大文件上传的终极指南:如何优雅处理GB级文件传输?
前端·javascript·面试