如何将nodejs制作的控制台程序打包成exe
本文由 小茗同学 发表于 2017-06-22 浏览(24526)
最后修改 2018-03-06 标签:nodejs exe

前言

虽然控制台程序本来就是没有界面的,打包成exe有点多余,但是如果想把写好的代码发给一个非程序员使用,可能还要教人家如何装node,如何运行…… 麻烦死了!

本文采用的方法是先用批处理包装,然后再将批处理打包成exe,批处理转exe的工具很多,我这里随便找了一个Bat To Exe Converter来实现。

正式开始

目标

我们肯定希望生成的exe是这样的:

  1. 双击即可运行,临时文件不能生成在当前目录(这样看起来才像个独立的exe);
  2. 退出程序时自动删除临时文件(不然时间越久临时目录越大);
  3. 可以任意设置图标;
  4. 虽然文件输出到临时目录,但是工作目录最好是exe的启动目录;

上面1、2、3这三点都比较好实现,关键是第4点。

测试文件

我们写一个最简单的控制台程序,只为了验证当前目录,test.js

console.log('当前工作目录:' + process.cwd());

批处理封装

批处理可以设置窗口标题以及字体颜色等,所以我们用批处理再包装一层:

@echo off
node test.js
pause

假设以上文件在D:\test下面,我们在E:\exe下面执行call D:\test\test.bat,很显然,由于批处理的默认当前目录在E盘,所以报错:

E:\exe>call D:\test\test.bat
module.js:471
	throw err;
	^

Error: Cannot find module 'E:\exe\test.js'
	at Function.Module._resolveFilename (module.js:469:15)
	at Function.Module._load (module.js:417:25)
	at Module.runMain (module.js:604:10)
	at run (bootstrap_node.js:394:7)
	at startup (bootstrap_node.js:149:9)
	at bootstrap_node.js:509:3

所以我们把批处理改成这样(同时为了后面测试方便,我们多输出了2个参数):

@echo off
echo %cd%
echo %~dpf0
node %~dp0%test.js
pause

运行之后正常:

E:\exe>call D:\test\test.bat
E:\exe
D:\test\test.bat
当前工作目录:E:\exe
请按任意键继续. . .

尝试使用好压打包成exe

使用好压的自解压模式可以轻松将批处理打包成exe(下图为900多kb的gif,耐心等待加载):

使用好压打包exe

只可惜打包之后,它的工作目录默认是解压的临时目录:

当前工作目录:C:\Users\Administrator\AppData\Local\Temp\HZ$D.816.1632
请按任意键继续. . .

尝试了一番还是没找到解决办法,所以只能放弃了,如果有人知道还烦请告知。

使用第三方工具

我使用了一个叫Bat To Exe Converter的工具:

配置好以上几步之后,点击左下角的编译就OK了:

为了对比,我们把双击生成的test.exe也移到E:\exe下面测试:

可以发现,这个工具把%cd%%~dpf0和普通方式对调了一下,工作目录是临时目录,而%0却成了启动exe的路径,好吧,这样也没关系,我们这样改造一番就OK了:

@echo off
:: 将当前目录保存起来
set tempPath=%cd%
:: 定位到exe所在目录
%~d0
cd %~dp0
:: 执行临时目录的js文件
node %tempPath%\test.js
pause

至此,我们完美地解决了最开始说的第4点问题^_^。

实例:nginx配置文件监听工具

别整些没用的,我们来点实际的。实际工作中经常需要改动nginx配置文件,每次改完都需要reload一下感觉很麻烦,于是简单写了个配置文件监听工具,一改动就自动刷新,代码如下:

nginx-conf-watcher.js:

const fs = require('fs');
const path = require('path');
const {exec, execFile, spawn} = require('child_process');

var lastModifiedTime = Date.now();
var args = process.argv.splice(2); // 获取命令行后面的参数
// 被监听的文件或者文件夹名字,path.resolve获取其相当于node启动路径的绝对路径
// 允许通过 node nginx-conf-watcher.js folderName 来指定被监听文件夹的名字
var watchFile = path.resolve(args[0] || 'conf');
console.log('\n提示:请务必将本程序放在和nginx.exe同一目录下,否则不会正常工作!\n');
try {
	// recursive 表示递归监听子文件夹的变化
	fs.watch(watchFile, {recursive: true}, function(eventType, fileName) {
		var now = Date.now();
		// 连续2次触发间隔小于50毫秒时忽略
		if(now - lastModifiedTime < 50) return;
		// 只监听conf文件的变化
		if(!fileName.endsWith('.conf')) return;
		lastModifiedTime = now;
		console.log(`\n检测到文件 ${fileName} 的变化,开始刷新nginx配置文件...`);
		var reload = spawn('./nginx', ['-s', 'reload']);
		reload.stdout.on('data', data => {});
		reload.stderr.on('data', data => {
			console.log('刷新nginx配置文件失败:');
			console.log(`${data}`);
		});
		reload.on('exit', (code) => {
			// 0 表示进程正常结束
			if(code == 0) console.log('刷新nginx配置文件成功!')
		});
	});
	console.log('nginx配置文件监听服务已启动!');
}
catch(e) {
	console.log(`监听“${watchFile}”文件(夹)失败,可能是文件不存在!`);
}

nginx-conf-watcher.bat:

@echo off
setlocal enabledelayedexpansion
title nginx配置文件监听工具v1.0
color 0A
mode con: cols=75 lines=30
:: 保存解压的临时目录
set tempPath=%cd%
:: 定位到exe文件所在盘
%~d0
:: 定位到exe文件所在路径
cd %~dp0
node %tempPath%\nginx-conf-watcher.js
pause

最后打包运行效果:

文件效果:

工具下载地址:http://file.haoji.me/detail/5

早些时候写了一篇水文,和本文有点关系,把链接也挂这吧:关于nodejs的启动路径和当前路径问题