You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于node-gyp的C++ Node Addon二进制文件读写失败问题解决

Node Addon集成fstream二进制文件写入失败的解决方案

我有一个用fstream实现二进制文件读写的C++类Controller.h,单独用gcc编译运行时,Insert函数能正常在data目录生成二进制文件。但把它集成到基于node-gyp开发的Node Addon后,调用Insert方法始终返回失败,其余逻辑都正常。相关代码如下:

相关代码

Controller.h

#pragma once
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include <stdio.h>

using namespace std;

template <typename T>
class Controller
{
private:
    T buf;
    char name[40]{};

public:
    explicit Controller(char *name) { strcpy(this->name, name); }
    char *GetName() { return name; }
    bool Insert(T model);
    //    int Search(T &bus);
    //    int Delete(T model);
};

template <typename T>
bool Controller<T>::Insert(T model)
{
    fstream file;
    string db_dir = "data//";
    string file_path = db_dir + name;
    file.open(file_path.c_str(), ios::out | ios::binary);

    if (file.fail() || file.bad())
    {
        cout << "Failed" << endl;
        return false;
    }
    file.write((char *)&model, sizeof(model));
    file.close();
    cout << "Successfully wrote" << endl;
    return true;
}

binding.gyp

{
    "targets": [{
        "target_name": "TDS",
        "cflags!": ["-fno-exceptions"],
        "cflags_cc!": ["-fno-exceptions"],
        "sources": [
            "modules/src/main.cpp"
        ],
        "include_dirs": [
            # "node_modules/node-addon-api",
            "<!(node -e \"require('nan')\")",
            "<!(node -p \"require('node-addon-api').include_dir\")"
        ],
        'libraries': [],
        'dependencies': [
            "<!(node -p \"require('node-addon-api').gyp\")"
        ],
        'defines': ['NAPI_DISABLE_CPP_EXCEPTIONS']
    }]
}

modules/src/main.cpp

#include <napi.h>
#include "controller/Controller.h"
#include "auth/Login.cpp"
#include "model/Structs.h"

using namespace Napi;
using namespace std;

Value InsertProducto(const CallbackInfo &info)
{
    Env env = info.Env();

    if (!info[0].IsObject())
    {
        TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
        return env.Null();
    }

    Object obj = info[0].ToObject();
    Array props = obj.GetPropertyNames();

    Producto producto = *new Producto();

    producto.id = obj.Get("id").ToNumber().Int32Value();
    producto.id_proveedor = obj.Get("id_proveedor").ToNumber().Int32Value();
    producto.stock = obj.Get("stock").ToNumber().Int64Value();
    producto.precio = obj.Get("precio").ToNumber().FloatValue();
    string descripcion = obj.Get("descripcion").ToString().Utf8Value();
    if (descripcion.length() > sizeof(producto.descripcion))
    {
        string mssg = "Excedeed maximum size: " +
                      sizeof(producto.descripcion);
        cout << mssg << endl;
        TypeError::New(env, mssg).ThrowAsJavaScriptException();
        return env.Null();
    }
    strcpy(producto.descripcion, descripcion.c_str());
    producto.stock_min = obj.Get("stock_min").ToNumber().Int64Value();

    cout << "Producto id: " << producto.id << endl;
    cout << "Producto id_proveedor: " << producto.id_proveedor << endl;
    cout << "Producto stock: " << producto.stock << endl;
    cout << "Producto precio: " << producto.precio << endl;
    cout << "Producto descripcion: " << producto.descripcion << endl;
    cout << "Producto stock_min: " << producto.stock_min << endl;

    Controller<Producto> controller((char *)"Cliente.dat");
    if (!controller.Insert(producto))
    {
        return String::New(env, "Failed");
    }
    return String::New(env, "Successfully inserted");
}
Object Init(Env env, Object exports)
{
    exports.Set(String::New(env, "InsertProducto"), Function::New<InsertProducto>(env));

    return exports;
}

// Register and initialize native add-on
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init);

JavaScript调用代码

var TDS = require('../build/Release/TDS.node');

const producto = {
    "id": "1",
    "id_proveedor": "1",
    "stock": "5",
    "precio": "50.0",
    "descripcion": "Un producto",
    "stock_min": "500"
}

console.log(TDS);
console.log(TDS.InsertProducto(producto));

module.exports = TDS;

解决方案

1. 自动创建data目录

Node Addon运行时,fstream不会自动创建不存在的目录,需要手动添加目录创建逻辑,兼容跨平台:

// 在Controller.h顶部添加头文件
#include <sys/stat.h>
#include <errno.h>

template <typename T>
bool Controller<T>::Insert(T model)
{
    fstream file;
    string db_dir = "data/"; // 改用单斜杠兼容跨平台

    // 创建目录,已存在时不会报错
    #ifdef _WIN32
        _mkdir(db_dir.c_str());
    #else
        mkdir(db_dir.c_str(), 0755);
    #endif

    string file_path = db_dir + name;
    file.open(file_path.c_str(), ios::out | ios::binary);

    if (!file.is_open()) {
        // 打印具体错误原因,方便排查
        cout << "Open failed: " << strerror(errno) << endl;
        return false;
    }
    file.write((char *)&model, sizeof(model));
    file.close();
    cout << "Successfully wrote" << endl;
    return true;
}

2. 修正相对路径问题

Node.js运行时的当前工作目录可能不是项目根目录,导致data/路径指向错误。可以先打印当前工作目录确认:

// 在main.cpp的InsertProducto函数中添加
#include <unistd.h> // Unix/Linux
// Windows替换为#include <direct.h>

Value InsertProducto(const CallbackInfo &info)
{
    // ... 其他代码
    char cwd[1024];
    if (getcwd(cwd, sizeof(cwd)) != NULL) {
        cout << "Current working dir: " << cwd << endl;
    } else {
        perror("getcwd error");
    }
    // ... 其他代码
}

如果路径不符,可以改用绝对路径,或者在JavaScript端通过process.cwd()传递正确的根目录给Addon。

3. 修复内存泄漏问题

原代码中Producto producto = *new Producto();会造成内存泄漏,直接声明栈对象即可:

// 替换为
Producto producto;

4. 优化错误判断逻辑

原代码用file.fail() || file.bad()判断不够准确,改用!file.is_open()更直接,同时结合strerror(errno)输出具体错误信息,快速定位问题(比如权限不足、路径不存在等)。

内容的提问来源于stack exchange,提问作者Jesus Jimenez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 12:40:23