标签归档:node.js

[node.js]同时使用canvas模块和sharp模块出现“The specified procedure could not be found”错误

升级到最新的node之后这俩模块是彻底没法共存了,之前还能通过降级其中一个模块来兼容一下,现在我怎么试都没法成功运行了,于是尝试找解决方案。

然后我发现这是由于两个模块使用的同名链接库不兼容导致的其中一个模块报错,似乎就是个无解的问题,偶然发现一个替代方案:https://github.com/Brooooooklyn/canvas

如果本来只使用了标准的绘制功能,那么可以连代码都不用改,直接替换掉依赖就可以解决问题,如果使用了非标准canvas方法,那么可能需要做一些小修改,比如将图片导出为png等格式时的额外参数之类的情况。

基本上要进行的操作就是删除package.json中的canvas依赖那行,然后直接执行npm i @napi-rs/canvas,npm就会自动删除canvas模块并安装@napi-rs/canvas,然后把代码中的require("canvas")换成require("@napi-rs/canvas")即可。

[expressjs]捕获路由和中间件的异步错误

express本身可以写async函数作为路由或者中间件(以下简称路由)使用,但如果在其中直接抛出异步错误的话则不会被捕获并传递给错误处理路由,需要在每一个async路由中手动写try/catch来捕获并处理错误,这显然对于拥有统一错误处理逻辑的框架是不友好的。

我不知道为什么express始终没有在4.x版本添加这个支持,看起来这应该并不会导致什么兼容性问题,即使不默认全局开启这个功能,也可以添加一个选项或开关函数来允许使用者开启异步错误捕获,但它始终没有这么做。

在这之前npm上也已经有一些可以实现这个功能的补丁包,不过它们几乎都是给创建出的app实例的所有相关路由方法套了一层壳在里面try/catch错误,导致如果是自己用一些特殊方法创建的路由对象,就失效了。

我原本用了一个直接修改express源码中相关原型方法的包,叫做express-async-errors,不过这个包没考虑到项目引用其它位置的express包的情况,它直接在代码中对相关源文件进行了require,导致无法作用于项目外依赖库的express,所以我重写了一个,而且源码比它还要简单,因为我直接把Layer类的handle方法改成了async函数,然后在里面await我们的路由函数,目前还没发现什么副作用,在我的项目中直接使用没有出现问题。

包地址:express-async-error-patch

asyncPatch函数需要传入一个创建出来的app实例, 为的是能获取到app所属的express包里的构造函数,不是为了在实例上包装方法。该函数在任何阶段调用都可以,只要在处理请求前调用即可。

[node.js]在redis上使用数据搜索

reids本身是一个基于键值对数据存储的内存数据库,也就是只能通过数据的key来获取数据项目,那么它自然也就没有任何数据搜索方面的功能,只能依靠一定规则生成的key来获取数据。

虽然它的本体是这样,但redis也提供了几个模块为其添加了一部分搜索功能的支持,并将这些模块整合为了redis-stack,官网介绍:https://redis.io/docs/stack/,我暂时还没测试它是否可以直接替代原本的redis实例,但就命令形式上来看,应该是兼容的。

本文是对于redis-stack官网上提供的node.js示例记录的笔记。

安装

redis-stack的安装我就不介绍了,直接从官网下载就好,或者使用docker之类的,本文只记录如何使用。

要在node.js中使用redis-stack的相关特性,我们需要redis-om模块,使用npm i redis-om安装进需要它的项目,关于该模块更详细的API介绍可以去其npm包页面上或者github仓库查看:https://github.com/redis/redis-om-node,不过要注意的是官方的示例中有些地方是错的,我会在下面对应的部分说明。 继续阅读[node.js]在redis上使用数据搜索

[SASS]指定import查找路径

本文为node包sass的使用笔记。

写这篇文章,是因为我碰到了引用node_modules目录中模块的scss文件的需求,但是它总是提示找不到: “Error: Can’t find stylesheet to import.”,于是我搜了搜,发现说是要在渲染参数中添加loadPaths数组来告诉sass去哪里找依赖文件,我在这个模块的typescript类型定义中也找到了这个参数名,但是我加上了之后并没有用,于是研究一番发现这个包使用的实际上并不是loadPaths,而是includePaths这个参数名,所以觉得有必要写篇笔记记录一下。

另外我发现有的解决方案中说可以在import的url前面加个~来表示这个文件在node_modules里,但我试了也没有用,这个特性似乎是webpack给sass引入的importer,所以单独使用sass的render api时此方式是不起作用的,如果使用的是sass-loader的话可以使用这个方法。

[Node.js]ES模块和CommonJS模块的导入

在使用ES导入语法的文件中想要使用require导入一个CommonJS模块的话,可以使用module.createRequire来创建一个require函数。

import { createRequire } from 'module';
const require = createRequire(import.meta.url);

 

在一般的使用CommonJS导入语法的文件中想要使用ES语法导入模块的话,可以使用import函数,要注意的是这是一个异步函数,返回一个Promise,所以需要await它或者使用then来等待返回结果,如下

(async()=>{
	const fs=await import('fs);
})();

 

[Browserify]异步插件导致write after end错误

写了个Browserify插件,之前一直没问题,但今天我改了点打包流程,结果一直报write after end错误,找了半个晚上,发现是因为我的插件函数写的是异步的,里面有个await异步读文件。

之前一直没出问题是因为后面的打包流程时间够长,能让这个插件正常执行完,但今天改了流程之后部分情况下很快就执行完了,于是当插件异步流程执行完后继续就出错了,修正方法是把异步文件读取改成同步的。

[Node.js]进程异常退出,错误码 3221226505

不是我想水一篇博文,在解决完前一篇博文的问题之后,立刻就又碰上另一个奇葩问题,进程直接跑着跑着没了,留下一个错误码3221226505。

在我左思右想把代码改来改去,包换来换去之后依然不能解决问题,然后发现是canvas包的问题,之所以一直没有发现是它,是因为出问题的代码里并没有用到这个包,它是在别的地方被引用到的,可能它的二进制模块破坏了node的环境,导致别的代码执行的时候触发了异常导致程序崩溃。

这个问题其实也不是第一次出现了,而且每次都是因为项目中同时有canvas和sharp两个模块才出现问题,在某些版本下不会出问题,某些版本下又会出现不一样的错误,这次就是和上次不一样所以才没能快速发现。

就这点破事,又浪费了我一个晚上,这时候我真想给搞出这bug的人屁股上来一脚。

[Node.js]解决‘gbk’ codec can’t decode byte 0x80 in position

每次换一个环境执行npm i都会碰到这样的问题,记录一下解决方法,

方法1:手动去报错的文件里指定编码为UTF-8,我自己第一次解决这个问题就是用的这个办法,刚刚想找找看有没有别的解决方法的时候发现别人也有去手动改源码的,放个参考链接

简单地说就是找到报错的那行,给open函数加个encoding='utf-8' 参数就可以解决问题,但这样属于改了人家文件,更新后还是会变回去的,所以现在我不这样做了。

方法2:去控制面板的区域设置里到“管理”标签页下,“更改系统区域设置”,把“Beta版:使用Unicode UTF-8提供全球语言支持(U)”勾上,然后重启一下,python就会默认用utf-8来读文件了。

这个方法会改变整个系统的默认代码页,最主要的影响是会导致使用非Unicode的程序乱码甚至崩溃,还有在gbk设置时以ansi保存的含有中文的bat脚本会乱码。如果影响不大的话倒是可以让系统一直保持在这个区域选项上,影响某些软件的正常使用的话在执行完需要的编译任务后还得改回去。在这种模式下可以使用“locale emulator”这样的软件以Chinese模式启动程序,以解决程序的乱码和崩溃问题。

方法3:我没试过,理论上应该可行,从python本体入手,把它的默认编码设置成utf-8,参考这里的方案2。这个没用

[Node.js]file-namer 文件重命名工具

昨晚写了个重命名工具,使用正则表达式匹配并替换文件名。

现在给npm包起个合适的名字是真难

GitHub: https://github.com/JiaJiaJiang/node-namer

效果

$ namer -f "/.+(?=\.txt$)/" -r "#COUNTERpoi$&"
match: /.+(?=\.txt$)/   find: /.+(?=\.txt$)/   replacement: #COUNTERpoi$&
Match list:
1.txt   >       1poi1.txt
2.txt   >       2poi2.txt
3.txt   >       3poi3.txt
4.txt   >       4poi4.txt
5.txt   >       5poi5.txt
6.txt   >       6poi6.txt
7.txt   >       7poi7.txt
8.txt   >       8poi8.txt
9.txt   >       9poi9.txt
10.txt  >       10poi10.txt
11.txt  >       11poi11.txt
12.txt  >       12poi12.txt
13.txt  >       13poi13.txt
14.txt  >       14poi14.txt
15.txt  >       15poi15.txt
16.txt  >       16poi16.txt
17.txt  >       17poi17.txt
18.txt  >       18poi18.txt
19.txt  >       19poi19.txt
20.txt  >       20poi20.txt
21.txt  >       21poi21.txt
22.txt  >       22poi22.txt
23.txt  >       23poi23.txt
24.txt  >       24poi24.txt
25.txt  >       25poi25.txt
26.txt  >       26poi26.txt
27.txt  >       27poi27.txt
28.txt  >       28poi28.txt
29.txt  >       29poi29.txt
30.txt  >       30poi30.txt
31.txt  >       31poi31.txt
32.txt  >       32poi32.txt
33.txt  >       33poi33.txt
34.txt  >       34poi34.txt
35.txt  >       35poi35.txt
36.txt  >       36poi36.txt


36matches found.
Confirm?    (control+c to exit)

Finished. 36succeeded,0failed

 

[Node.js]拦截process.stdout和process.stderr

由于解决这个问题花了我一些时间,所以记录一下说不定可以帮到其他人。

process.stdout是一个getter,所以我们不能用普通的替换来换掉process.stdout来拦截写入它的数据。同时,process.stdout是一个Writable Stream,所以也不能简单地直接从它里面获取写入的数据。

 

一开始我花了不少时间来研究怎么可以从这个Writable Stream里读出数据,但是这似乎太麻烦了,然后我甚至想到了利用child process来拦截数据。最后发现其实很简单,我们只要重新定义这个Getter就可以了。

var stream=require('stream');


var rawStdout=process.stdout,//先拿到原来的stdout
	newStdout=new stream.PassThrough();//创建一个passthrough流,这是一种特殊的Transform流,会直接把写入的数据吐出来
process.__defineGetter__('stdout',function(){//重新定义process.stdout的Getter
	return newStdout;//返回我们的passthrough流
});

这样我们就成功拦截到标准输出了,要注意,这段代码必须放在有任何输出之前,一旦在之前有了内容输出,它就没用了。

然后我们就可以自己决定怎么处理stdout了比如:

newStdout.pipe(rawStdout);//内容输出到控制台

newStdout.pipe(文件的writable stream);//内容写入文件

newStdout.pipe(其它可写流);//随你怎么处理

 

 

同理,process.stderr也可以这样拦截

[Node.js]防止node自动退出

node执行完所有代码以后就会退出(部分监听服务除外),如果不希望node立刻退出,只要在任意位置加上一行

setInterval(function(){},9999999);//时间设置多少都没有关系,这只影响这个空回调函数的调用频率

node就不会退出了(除非出错

如果你希望即使是出错了,也不让Node退出,那么就需要加入以下事件监听器

process.on('uncaughtException',(err,origin)=>{//捕捉uncaughtException
	console.error('[uncaughtException]',err,origin);
});

process.on('unhandledRejection',(err,promise)=>{//捕捉unhandledRejection
	console.error('[unhandledRejection]',err);
});

加入这两个监听器之后,node遇到任何未捕捉的错误,都会交给第一个监听器处理,碰到任何未catch的异常Promise,都会交给第二个监听器处理,你可以在里面做些事情,比如打个日志之类的。

==========更新log===========

  • 2021/11/16: 加入出错时不退出的方法

node被抛弃,iojs取而代之

从node分离出来农民起义的iojs终于取得了革命胜利。现在node的版本一下从0.12.x飞升到了4.0.0。

Node.js ChangeLog
2015-09-08, Version 4.0.0 (Stable), @rvagg

Notable changes

This list of changes is relative to the last io.js v3.x branch release, v3.3.0. Please see the list of notable changes in the v3.x, v2.x and v1.x releases for a more complete list of changes from 0.12.x. Note, that some changes in the v3.x series as well as major breaking changes in this release constitute changes required for full convergence of the Node.js and io.js projects.

child_process: ChildProcess.prototype.send() and process.send() operate asynchronously across all platforms so an optional callback parameter has been introduced that will be invoked once the message has been sent, i.e. .send(message[, sendHandle][, callback]) (Ben Noordhuis) #2620.
node: Rename "io.js" code to "Node.js" (cjihrig) #2367.
node-gyp: This release bundles an updated version of node-gyp that works with all versions of Node.js and io.js including nightly and release candidate builds. From io.js v3 and Node.js v4 onward, it will only download a headers tarball when building addons rather than the entire source. (Rod Vagg) #2700
npm: Upgrade to version 2.14.2 from 2.13.3, includes a security update, see https://github.com/npm/npm/releases/tag/v2.14.2 for more details, (Kat Marchán) #2696.
timers: Improved timer performance from porting the 0.12 implementation, plus minor fixes (Jeremiah Senkpiel) #2540, (Julien Gilli) nodejs/node-v0.x-archive#8751 nodejs/node-v0.x-archive#8905
util: The util.is*() functions have been deprecated, beginning with deprecation warnings in the documentation for this release, users are encouraged to seek more robust alternatives in the npm registry, (Sakthipriyan Vairamani) #2447.
v8: Upgrade to version 4.5.103.30 from 4.4.63.30 (Ali Ijaz Sheikh) #2632.
Implement new TypedArray prototype methods: copyWithin(), every(), fill(), filter(), find(), findIndex(), forEach(), indexOf(), join(), lastIndexOf(), map(), reduce(), reduceRight(), reverse(), slice(), some(), sort(). See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray for further information.
Implement new TypedArray.from() and TypedArray.of() functions. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray for further information.
Implement arrow functions, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions for further information.
Full ChangeLog available at https://github.com/v8/v8-git-mirror/blob/4.5.103/ChangeLog

以上是搬过来的changeLog,同时在changeLog页面往下翻一番,全都是iojs之前的版本号,很明显node原本的项目是直接被iojs项目覆盖掉了,注意这一行

node: Rename "io.js" code to "Node.js" (cjihrig) #2367.

iojs重命名为nodejs,革命军变成了正规军,推翻了原本的帝国统治。

现在点开iojs.org官网里的changelog也是直接跳转到https://github.com/nodejs/node/blob/master/CHANGELOG.md,也就是node的changelog。

 

这对native mod开发者来说是个福音,不用再去考虑兼容iojs和node携带的两个版本差距悬殊的v8版本了。