NAPI 类对象导出及其生命周期管理(下)

news2024/11/22 6:32:35

4. 样例工程源码剖析

  • 工程的模板是Native C++,模型是Stage
  • 源码剖析主要围绕以下几个文件

4.1. NAPI导出对象和生命周期管理具体实现

4.1.1. 定义NapiTest类及方法
  • Napi.h文件内容如下:
#ifndef __NAPI_TEST_H__
#define __NAPI_TEST_H__

#include "napi/native_api.h"
#include <js_native_api_types.h>

#include <iostream>

#define NAPI_CLASS_NAME "NapiTestClass"

class NapiTest {
public:
NapiTest() : mEnv(nullptr), mRef(nullptr) {
}
  
NapiTest(napi_env env) : mEnv(env), mRef(nullptr){
}
~NapiTest();
  
  // 创建NapiTest类的实体,并将实体返回到应用端,该方法为js创建一个类实体,因此需要将该接口对外导出
static napi_value Create(napi_env env, napi_callback_info info);
  
  // 初始化js类并设置对应属性并将其导出   
static napi_value Init(napi_env env, napi_value exports);         


private:
  
  // 设置数据,此方法给到js直接调用,因此需要将该接口对外导出
  static napi_value SetMsg(napi_env env, napi_callback_info info);
  
  // 获取数据,此方法给到js直接调用,因此需要将该接口对外导出    
  static napi_value GetMsg(napi_env env, napi_callback_info info);
  
  // 定义js结构体时实际的构建函数
  static napi_value Constructor(napi_env env, napi_callback_info info);     

  // 释放资源的函数(类似类的析构函数)    
  static void Destructor(napi_env env, void *nativeObject, void *finalize); 

  // 生命周期变量    
  static napi_ref sConstructor_;  

  // 设置和获取数据的变量    
  static std::string _msg;        

  // 记录环境变量    
  napi_env mEnv = nullptr;        

  // 记录生命周期变量    
  napi_ref mRef = nullptr;        

};

#endif  /* __NAPI_TEST_H__ */
4.1.1.1 napi_value
  • Node.js Node-API的值用napi_value类型表示。
    OpenHarmony NAPI将ECMAScript标准中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型,以及函数对应的Function类型,统一封装成napi_value类型,下文中表述为JS类型,用于接收ArkUI应用传递过来的数据及返回数据给ArkUI应用。
  • 这是一个不透明的指针,用于表示JavaScript值。
4.1.1.2 napi_ref
  • 这是用来引用napi_value的抽象。这允许用户管理JavaScript值的生命周期,包括显式地定义它们的最小生命周期。
4.1.1.3 napi_env
  • napi_env用于表示上下文,底层的Node-API实现可以使用该上下文持久保持VM-specific的状态。
4.1.2 将NapiTest类定义为js类
4.1.2.1在定义js类之前,需要先设置js类对外导出的方法
    // 在定义js类之前,需要先设置类对外导出的方法
    napi_property_descriptor desc[] = {
        { "getMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "setMsg", nullptr, NapiTest::SetMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "create", nullptr, NapiTest::Create, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
4.1.2.1.1 napi_property_descriptor

Node.js Node-API有一组API来获取和设置JavaScript对象的属性。在JavaScript中,属性被表示为一个键和一个值的元组。基本上,Node-API中的所有属性键都可以用以下形式中的任一一种表示:

  • Named:一个简单的UTF-8编码的字符串
  • Integer-Indexed:索引值,由uint32_t表示
  • JavaScript value:在Node-API中通过napi_value表示。它可以是一个napi_value,表示字符串、数字或符号。
typedef struct {
  // utf8name和name其中一个必须是NULL
  const char* utf8name;
  napi_value name;

  napi_callback method;
  napi_callback getter;
  napi_callback setter;
  napi_value value;

  napi_property_attributes attributes;
  void* data;
} napi_property_descriptor;

参数解析:

  • utf8name:在定义js类之前设置的js类对外导出的方法名字,编码为UTF8。必须为该属性提供utf8name或name中的一个。(utf8name和name其中一个必须是NULL)
  • name:可选的napi_value,指向一个JavaScript字符串或符号,用作属性的键。必须为该属性提供utf8name或name中的一个。
  • method:将属性描述符对象的value属性设置为method表示的JavaScript函数。如果传入这个参数,将value、getter和setter设置为NULL(因为这些成员不会被使用)。
  • attributes:与特定属性相关联的属性。
  • data:调用函数时传递给method、getter和setter的callback data。
4.1.2.2 定义与C++类相对应的JavaScript类
    napi_value constructor = nullptr;

    // 定义与C++类相对应的JavaScript类
    if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(desc[0]),
                          desc, &constructor) != napi_ok) {
        // "!="用来检查两个操作数的值是否相等,如果不相等则条件为真
        return nullptr;
    }
4.1.2.2.1 napi_define_class

napi_define_class函数说明:

napi_status napi_define_class(napi_env env,
                          const char* utf8name,
                          size_t length,
                          napi_callback constructor,
                          void* data,
                          size_t property_count,
                          const napi_property_descriptor* properties,
                          napi_value* result);

功能:定义与C ++ 类相对应的JavaScript类。
参数说明:

  • [in] env: 调用api的环境
  • [in] utf8name: C ++ 类的名称
  • [in] length: C ++ 类的名称的长度,默认自动长度使用NAPI_AUTO_LENGTH
  • [in] constructor: 处理C ++ 类实例构造的回调函数 (因为Constructor函数被napi_define_class调用了)。在导出C ++ 类对象时,这个函数必须是带有napi_callback签名(Constructor函数有napi_callback签名是指要满足typedef napi_value (*napi_callback)(napi_env, napi_callback_info);的形式)的静态成员。不能使用c ++ 的类构造函数。
  • [in] data: 作为回调信息的数据属性传递给构造函数回调的可选数据
  • [in] property_count: 属性数组中参数的个数
  • [in] properties: 属性数组,具体看代码中napi_property_descriptor部分
  • [out] result: 通过类构造函数绑定类实例的napi_value对象
    返回:如果API调用成功返回napi_ok。

JS构造函数
如果一个js函数被使用new操作符来调用了,那么这个函数就称之为js构造函数

C++类回调函数
我们调用别人的API叫call,调用的第三方API调用我们的函数叫回调(callback)

4.1.2.3 实现js类的构造函数

当ArkTS应用在js端通过new方法获取类对象的时候,此时会调用 napi_define_class 中设置的 constructor 回调函数,该函数实现方法如下:

napi_value NapiTest::Constructor(napi_env env, napi_callback_info info)
{
    napi_value undefineVar = nullptr, thisVar = nullptr;
    napi_get_undefined(env, &undefineVar);

    // 获取传入的参数对象,对象不为空,根据该参数创建实例并并绑定到该对象
    if (napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr) == napi_ok && thisVar != nullptr) {

        // 创建NapiTest 实例
        NapiTest *reference = new NapiTest(env);

        // 绑定实例到对象并获取对象的生命周期
        if (napi_wrap(env, thisVar, reinterpret_cast<void *>(reference), NapiTest::Destructor, nullptr, &(reference->mRef)) == napi_ok) {
            return thisVar;
        }

        return thisVar;
    }
    
    return undefineVar;
}

void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{
    // 释放资源
    NapiTest *test = reinterpret_cast<NapiTest*>(nativeObject);
    test->~NapiTest();
}
  • NapiTest::Destructo方法是用来释放创建的对象:
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{
  // 类析构函数,释放资源
  NapiTest *test = reinterpret_cast<NapiTest*>(nativeObject);
  test->~NapiTest();
}
4.1.2.3.1 napi_wrap
napi_status napi_wrap(napi_env env,
                  napi_value js_object,
                  void* native_object,
                  napi_finalize finalize_cb,
                  void* finalize_hint,
                  napi_ref* result);

功能:将C++类实例绑定到js对象,并关联对应的生命周期
参数说明:

  • [in] env: 调用api的环境
  • [in] js_object: 绑定native_object的js对象
  • [in] native_object: C++类实例对象
  • [in] finalize_cb: 释放实例对象的回调函数
  • [in] finalize_hint: 传递给回调函数的数据
  • [out] result: 绑定js对象的引用

返回:调用成功返回0,失败返回其他

4.1.2.3.2 napi_get_cb_info

NAPI提供了napi_get_cb_info()方法可从napi_callback_info中获取参数列表、this及其他数据。这个方法在constructor回调函数中使用,从给定的回调信息中检索有关调用的详细信息,如参数和This指针。

napi_status napi_get_cb_info(napi_env env,              
                             napi_callback_info cbinfo, 
                             size_t* argc,                          
                             napi_value* argv,     
                             napi_value* this_arg, 
                             void** data)     

参数说明:

  • [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可
  • [in] cbinfo: napi_callback_info对象,上下文的信息
  • [in-out] argc: argv数组的长度。若napi_callback_info中实际包含的参数的个数大于请求的数量argc,将只复制argc的值所指定数量的参数只argv中。若实际的参数个数小于请求的数量,将复制全部的参数,数组多余的空间用空值填充,并将参数实际长度写入argc。
  • [out] argv: 用于接收参数列表
  • [out] this_arg: 用于接收this对象
  • [out] data: NAPI的上下文数据 返回值:返回napi_ok表示转换成功,其他值失败。下面的返回napi_status方法一样。
4.1.3 导出js类
    // 创建生命周期,初始引用计数设为1
    if (napi_create_reference(env, constructor, 1, &sConstructor_) != napi_ok) {
        return nullptr;
    }

    // 设置NapiTest对象相关属性并绑定到导出变量exports
    if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {
        return nullptr;
    }
4.1.3.1 在设置js类导出前,需要先创建生命周期
if (napi_create_reference(env, constructor , 1, &sConstructor_) != napi_ok) {
    return nullptr;
}
  • constructor 定义js类时返回的代表类的构造函数的数据
  • sConstructor_ 生命周期变量
4.1.3.1.1 napi_create_reference

napi_create_reference为对象创建一个reference,以延长其生命周期。调用者需要自己管理reference生命周期。

napi_create_reference函数说明:

NAPI_EXTERN napi_status napi_create_reference(napi_env env,
                                              napi_value value,
                                              uint32_t initial_refcount,
                                              napi_ref* result);

功能:通过引用对象创建新的生命周期引用对象

  • [in] env: 调用 API 的环境
  • [in] value: napi_value表示我们要引用的对象
  • [in] initial_refcount: 生命周期变量的初始引用计数
  • [out] result: 新建的生命周期引用对象
    返回 napi_ok 这个API就是成功的.
4.1.3.2 将生命周期变量作为导出对象的传入属性,并将js类导出到exports中
//  设置constructor对象相关属性并绑定到导出变量exports
if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) !=  napi_ok) {
    return nullptr;
}
4.1.3.2.1 napi_set_named_property

为给定对象的属性设置一个名称。

napi_status napi_set_named_property(napi_env env,
                                    napi_value object,
                                    const char* utf8Name,
                                    napi_value value);
  • [in] env: 调用API的环境
  • [in] object: NapiTest对象相关属性要绑定的属性值
  • [in] utf8Name: js类的名称
  • [in] value: 要引用的对象
    返回 napi_ok 则这个API是成功的
4.1.3.3 设置导出对象的属性

hello.cpp中

    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
4.1.3.3.1 napi_define_properties
napi_status napi_define_properties(napi_env env,
                                   napi_value object,
                                   size_t property_count,
                                   const napi_property_descriptor* properties);

作用:批量的向给定Object中定义属性

  • [in] env: 调用api的环境
  • [in] object: js对象相关属性的导出变量
  • [in] property_count: 属性数组中的元素数
  • [in] properties: 属性数组
4.1.4 创建类的实例对象
  • ArkTS应用除了调用new方法获取类的实例外,我们也可以提供一些方法让ArkTS应用获取对应的类的实例,如在我们的NapiTest类中,定义了一个Create方法,该方法实现了NapiTest类实例的获取。具体实现如下:
napi_value NapiTest::Create(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value constructor = nullptr, result = nullptr;
    // 获取生命周期变量
    status = napi_get_reference_value(env, sConstructor_, &constructor);

    // 创建生命周期内的实例对象并将其返回
    status = napi_new_instance(env, constructor, 0, nullptr, &result);
    auto napiTest = new NapiTest();
    // 绑定实例类创建NapiTest到导出的对象result
    if (napi_wrap(env, result, reinterpret_cast<void *>(napiTest), Destructor,
        nullptr, &(napiTest->mRef)) == napi_ok) {
        return result;
    }
    
    return nullptr;
}
  • 在napi接口的注册中将该方法以接口的方式导出,应用层就可以直接调用该接口并获取到该类的实例对。
    特别说明:如果单独实现了一个类实例获取的方法,那么js的类构造函数可以不实现(也就是定义js结构体时实际的构建函数Constructor及释放资源的函数Destructor的代码够可以不写)
4.1.4.1 napi_get_reference_value

函数说明:

NAPI_EXTERN napi_status napi_get_reference_value(napi_env env,
                                                 napi_ref ref,
                                                 napi_value* result);
  • 作用:获取与reference相关联的js对象
  • [in] env: 调用API的环境
  • [in] ref: 生命周期管理的变量
  • [out] result: 对象引用的reference.
4.1.4.2 napi_new_instance
napi_status napi_new_instance(napi_env env,
                              napi_value cons,
                              size_t argc,
                              napi_value* argv,
                              napi_value* result)
  • 作用:通过给定的构造函数,构建一个对象
  • [in] env: 调用API的环境
  • [in] cons: napi_value表示要作为构造函数调用的 JavaScript 函数
  • [in] argc: argv 数组中的元素计数
  • [in] argv: JavaScript 值数组,表示构造函数的参数napi_value。
  • [out] result: napi_value表示返回的 JavaScript 对象

4.2 index.d.ts声明文件编写

使用NAPI框架代码生成工具,可以根据.h生成.d.ts

export const create : () => NapiTest;
export class  NapiTest {
    setMsg(msg: string): void;
    getMsg(): string;
}

也可以写成

export class  NapiTest {
    create();
    setMsg(msg: string): void;
    getMsg(): string;
}

4.3 CMakeLists.txt文件

# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(ObjectWrapTest)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

# 头文件路径
include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)
# 动态库源文件
add_library(entry SHARED hello.cpp NapiTest.cpp)
# 依赖libace_napi.z.so动态库
target_link_libraries(entry PUBLIC libace_napi.z.so )

4.4 index.ets文件

// 让IDE不检查文件语法
// @ts-nocheck 
import testNapi from "libentry.so";

@Entry
@Component

struct Index {
  @State message: string = '导出对象'
  @State nativePointer:number = 0

// 创建对象tt
  tt = testNapi.create();

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            console.info("[NapiTest] Test NAPI 2 + 3 = " + testNapi.add(2, 3));
            try{
              if (this.nativePointer == 0) {
                // log打印,在程序中添加 log
                console.info("[NapiTest] Test NAPI add(2, 3) 1");
                this.nativePointer = testNapi.add(2, 3)
                console.info("[NapiTest] Test NAPI add(2, 3) 2");

                this.tt.setMsg("2+3")
                console.info("[NapiTest] Test NAPI add(2, 3) 3");

              } else {

                console.info("[NapiTest] Test NAPI add(0, 0) 1");

                this.nativePointer = testNapi.add(0, 0)
                console.info("[NapiTest] Test NAPI add(0, 0) 2");

                this.tt.setMsg("4+5")
                console.info("[NapiTest] Test NAPI add(0, 0) 3");
              }
            } catch(e) {
              console.info("[NapiTest]Test NAPI error" + JSON.stringify(e));
            }
            console.info("[NapiTest]Test NAPI " + this.tt.getMsg() + " = " + this.nativePointer);
          })
      }
      .width('100%')
    }
    .height('100%')
  }

}

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1589396.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一款自研Python解释器

项目简介: PikaScript是一个完全重写的超轻量级python引擎,具有完整的解释器,字节码和虚拟机架构,可以在少于4KB的RAM下运行,用于小资源嵌入式系统。相比同类产品,如MicroPython,LuaOS等,资源占用减少85%以上。 入选2021年度 Gitee最有价值开源项目,加入RT-Thread嵌入…

从0开始创建单链表

前言 这次我来为大家讲解链表&#xff0c;首先我们来理解一下什么是单链表&#xff0c;我们可以将单链表想象成火车 每一节车厢装着货物和连接下一个车厢的链子&#xff0c;单链表也是如此&#xff0c;它是将一个又一个的数据封装到节点上&#xff0c;节点里不仅包含着数据&…

【c语言】结构体的访问

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;C语言 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&…

力扣HOT100 - 41. 缺失的第一个正数

解题思路&#xff1a; 原地哈希 就相当于&#xff0c;让每个数字n都回到下标为n-1的家里。 而那些没有回到家里的就成了孤魂野鬼流浪在外&#xff0c;他们要么是根本就没有自己的家&#xff08;数字小于等于0或者大于nums.size()&#xff09;&#xff0c;要么是自己的家被别…

51-37 由浅入深理解 Stable Diffusion 3

2024年3月5日&#xff0c;Stability AI公开Stable Diffusion 3论文&#xff0c;Scaling Rectified Flow Transformers for High-Resolution Image Synthesis。公司像往常一样承诺后续将开源代码&#xff0c;开源之光&#xff01;&#xff01;&#xff01; 在LDW潜在扩散模型论文…

学习51单片机必备:从电子基础到编程技巧全解析

学习51单片机需要掌握一系列的基础知识和技能&#xff0c;以下是一些主要的学习内容&#xff1a; 电子基础知识 了解基本的电子元件和电路原理是学习单片机的基础。这有助于理解单片机如何与外围设备交互以及如何设计电路。 数字逻辑 理解数字逻辑和布尔代数&#xff0c;对于编…

第十三届蓝桥杯真题:x进制减法,数组切分,gcd,青蛙过河

目录 x进制减法 数组切分 gcd 青蛙过河 x进制减法 其实就是一道观察规律的题。你发现如果a这个位置上的数x&#xff0c;b这个位置上的数是y&#xff0c;那么此位置至少是max(x,y)1进制。一定要把位置找对啊 #include <bits/stdc.h> using namespace std; typedef l…

如何恢复 iPhone 删除的照片?

照片是iPhone空间不足的主要原因&#xff0c;因此许多用户选择删除一些重复或不喜欢的图片来释放设备。然而&#xff0c;人们在清洁过程中不小心遗漏了自己喜欢的照片的情况很常见。如果你找不到这些珍贵的照片&#xff0c;你一定很难过。其实&#xff0c;您不必担心这个问题&a…

echarts 多环形图

环形图效果&#xff1a; option {"angleAxis": {"max": 1,"show": false,"splitLine": {"show": false},"axisLabel": {"show": false},"axisTick": {"show": false}},"ra…

Idea显示无法自动装配。找不到‘ xxx’类型的Bean

虽然只标红&#xff0c;不报错&#xff0c;但是看着非常别扭&#xff01; 原因&#xff1a; 当我们在使用Autowired注解的时候&#xff0c;默认requiredtrue,表示注入的时候bean必须存在&#xff0c;否则注入失败。 解决方案一&#xff1a; 在自动转配的注解后面添加(require…

低频电磁仿真 | 新能源汽车性能提升的利器

永磁同步电机 新能源汽车的心脏 近年来&#xff0c;全球变暖的趋势日益加剧&#xff0c;极端天气事件层出不穷&#xff0c;这些现象都反映出当前气候形势的严峻性。为了应对这一全球性挑战&#xff0c;各国纷纷采取行动&#xff0c;制定了一系列降碳、减碳的措施。中国在2020年…

2024年淘宝天猫京东618超级红包领取入口

2024年淘宝天猫京东618超级红包领取入口 1、打开「词令」&#xff0c;输入词令关键词直达口令「超红88」&#xff1b; 2、搜索直达2024年淘宝天猫、京东618超级红包领取入口&#xff1b;

新天龙八部3永恒经典之江山策仿官方_源码架设教程

本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#xff0c;踩过的坑都给你们填上了 一. 效果演示 新天龙…

解锁电气数据新价值:SolidWorks Electrical助力企业转型

在信息化、数字化的时代&#xff0c;电气数据库已成为企业不可或缺的核心资产。它以其独特的功能和优势&#xff0c;助力企业在激烈的市场竞争中脱颖而出&#xff0c;实现数字化转型的跨越式发展。 SolidWorks Electrical电气数据库具备强大的数据整合能力。它能够将企业内部各…

揭秘大前端开发方向的新机遇!

众所周知&#xff0c;华为开发者大会2023&#xff0c;宣布不再兼容安卓&#xff0c;同时宣布了“鸿飞计划”&#xff0c;欲与iOS、安卓在市场三分天下&#xff0c;这对中国国产操作系统而言&#xff0c;具有划时代的意义。 鸿蒙应用开发的兴起&发展 鸿蒙操作系统是华为自…

【解决】安装模块时报错:ERROR: *.whl is not a valid wheel filename.

其实错误信息已经告诉你了&#xff0c;就是你的文件名有问题。在你下载whl文件时一定要注意原文件的文件名&#xff0c;不要改动文件名。 以我安装pandas模块为例吧。 在我下载whl文件时&#xff0c;因为网速太慢&#xff0c;我就下载了多次&#xff0c;导致文件名变成了这个…

SpringBoot 中的日志原来是这么工作的

在有些场景&#xff0c;能通过调整日志的打印策略来提升我们的系统吞吐量,你知道吗&#xff1f; 我们以Springboot集成Log4j2为例&#xff0c;详细说明Springboot框架下Log4j2是如何工作的&#xff0c;你可能会担心&#xff0c;如果是使用Logback日志框架该怎么办呢&#xff1…

01_QT编译报错:Cannot find file:问题解决

QT编译报错&#xff1a;Cannot find file:问题解决 报错原因&#xff1a;创建路径存在中文字符&#xff0c;将文件路径改为英文字符即可

睿贝外贸ERP appPatchDownLoad 任意文件读取漏洞复现

0x01 产品简介 睿贝外贸ERP软件专门为中小型外贸企业打造的一款界面简洁、易用、操作人性,几乎实现零培训, 彻底颠覆其他软件漫长的实施周期。主要功能包括邮件管理、客户管理、邮营销、产品、供应商,报价、订单、出运、单证、财务、利润分析等一体化管理,以及Excel格式单…

防错设计及原理

目录 1、防错的作用 2、防错的原理 2.1断根原理 2.2保险原理 2.3自动原理 2.4相符原理 2.5顺序原理 2.6隔离原理 2.7层别原理 2.8复制原理 2.9警告原理 2.10缓和原理 防错法&#xff08;Poka-Yoke&#xff09;&#xff0c;又称愚巧法、防呆法&#xff0c;是一种在作…