```js
/**
-
Nodejs File Pack Help by cmd1152
-
开源许可证:MIT
-
-
node pack /files some_file.txt /output some_file.bat
-
/files [file1] <file2> … 传入要被嵌入的文件
-
/dir <dir> 传入目录结构,不常用,现在会自动检测叶子目录
-
/output [file_name] 要输出的批处理文件
-
/run [cmd] 附加命令到文件,一般用于释放后执行程序
-
/del 批处理执行完毕后删除自己
-
一些高级玩法
-
-
- 安装程序
-
node pack /files A /output A.bat /run A\setup
-
这是 A 文件夹的目录结构
-
A\
-
APP_NAME\
-
setup.bat // 调用 setup_ink 释放快捷方式,下一级是文件内容
@echo off
cd "%~dp0"
start /w "" "setup_ink.vbs"
start mshta vbscript:msgbox("MY APP installation completed successfully",64,"MY APP")(window.close)
del setup_ink.vbs
del "%~0" & exit
-
shell.bat // 调用入口
-
ink.ico // 快捷方式图标
-
setup_ink.vbs //释放快捷方式,下一级是文件内容
Set objShell = CreateObject("WScript.Shell")
currentDir = objShell.CurrentDirectory
targetPath = currentDir & "\shell.bat"
iconPath = currentDir & "\ink.ico"
desktopPath = objShell.SpecialFolders("Desktop") & "\My APP.lnk"
Set shortcut = objShell.CreateShortcut(desktopPath)
shortcut.TargetPath = targetPath
shortcut.WorkingDirectory = currentDir
shortcut.IconLocation = iconPath
shortcut.Save
-
... // 其他文件
-
setup.bat // 用于复制文件到安装目录下,然后运行里面的 setup.bat 进一步安装,下一级是文件内容
@echo off
cd "%~dp0"
echo;D|xcopy /Y /E APP_NAME "%HOMEDRIVE%%HOMEPATH%\APP_NAME"
set "a=%cd%"
cd /d "%HOMEDRIVE%%HOMEPATH%\APP_NAME"
start "" "setup.bat"
cd "%a%"
cd ..
rd /s /q "A" & exit
-
然后按照命令构建,得到一个bat文件
构建开始
树形扫描展开中
已展开:
叶子目录:
A\APP_NAME
文件:
A\APP_NAME\ink.ico
A\APP_NAME\shell.bat
A\APP_NAME\setup.bat
A\APP_NAME\setup_ink.vbs
A\APP_NAME\setup.bat
...
构建文件中
Packing - A\APP_NAME\ink.ico
转换cab - A\APP_NAME\ink.ico
...
正在保存 A.bat
构建完成
-
在一个新环境双击,可以发现桌面多了个快捷方式,这就是安装成功了
*/
const fs = require(‘fs’);
const path = require(‘path’);
const { exec } = require(‘child_process’);
const argv = process.argv.slice(2);
const args = {};
argv.forEach((arg, index) => {
if (arg.startsWith(“/”)) {
let endIndex = argv.slice(index + 1).findIndex((arg) => arg.startsWith(‘/’)) + 1;
endIndex = endIndex == 0 ? undefined : index + endIndex;
args[arg.substring(1)] = argv.slice(index + 1, endIndex);
}
});
if (!args.files || args.files.length == 0) {
console.error(缺少 /files 指定需要嵌入的文件
);
process.exit(1);
}
if (!args.output) {
console.error(缺少 /output 指定需要输出的文件
);
process.exit(1);
}
if (!args.output[0].toLowerCase().endsWith(‘.bat’) && !args.output[0].toLowerCase().endsWith(‘.cmd’)) {
console.error(/output 文件必须是批处理格式
);
process.exit(1);
}
if (args.files && args.files.some((filepath) => filepath.includes(“/”))) {
console.error(你似乎在使用 / 作为路径分割符号,请修改为 \\
);
process.exit(1);
}
if (!args.dir && (args.files && args.files.some((filepath) => filepath.includes(“\”)))) {
console.error(你没有使用 /dir 创建目录树,但是在文件路径使用了路径符号,如果你希望打包目录,请直接写进去目录名\n也可能是你使用的路径不是相对路径导致的错误
);
process.exit(1);
}
function readDirectory(dir, parentPath = ‘’, result = ) {
const fullPath = path.join(parentPath, dir);
const items = fs.readdirSync(fullPath);
let isLeaf = true;
items.forEach(item => {
const itemPath = path.join(fullPath, item);
const stats = fs.statSync(itemPath);
if (stats.isDirectory()) {
isLeaf = false;
readDirectory(item, fullPath, result);
}
});
if (isLeaf) {
result.push(fullPath.replace(///g, ‘\’));
}
return result;
}
async function readDirectoryRecursively(dir) {
const filePaths = ;
try {
const files = fs.readdirSync(dir, { withFileTypes: true });
for (const file of files) {
const filePath = path.join(dir, file.name);
if (file.isDirectory()) {
const subDirPaths = await readDirectoryRecursively(filePath);
filePaths.push(...subDirPaths);
} else {
filePaths.push(filePath.replace(/\//g, '\\'));
}
}
} catch (err) {
console.error( 无法读取目录 ${dir}: ${err}
);
}
return filePaths;
}
async function makeBase64(file) {
console.log( Packing - ${file}
);
const cabFilePath = path.join(path.dirname(file), ${path.basename(file)}.cab
);
await createCab(file, cabFilePath);
let data = fs.readFileSync(cabFilePath);
let b64 = data.toString(‘base64’);
fs.unlinkSync(cabFilePath);
return makeUnzip(b64, file);
}
async function createCab(filePath, cabFilePath) {
return new Promise((resolve, reject) => {
console.log( 转换cab - ${filePath}
);
const command = makecab "${filePath}" "${cabFilePath}"
;
exec(command, (error, stdout, stderr) => {
if (error) {
reject(new Error(`makecab failed: ${error.message}\n${stderr}`));
} else {
resolve(stdout);
}
});
});
}
function makeUnzip(b64, filepath) {
return [
(
,
b64.split(/(\n|.{1,3000})/).filter(d => d).map((line) => echo;${line}
).join(“\r\n”),
)>"${filepath.replace(/"/g, '\\"')}.b64"
,
IF EXIST "${filepath.replace(/"/g, '\\"')}" del "${filepath.replace(/"/g, '\\"')}"
,
certutil -decode "${filepath.replace(/"/g, '\\"')}.b64" "${filepath.replace(/"/g, '\\"')}.tmp" > nul
,
IF NOT EXIST "${filepath.replace(/"/g, '\\"')}.tmp" echo;Failed to Create ${filepath}
,
expand "${filepath.replace(/"/g, '\\"')}.tmp" "${filepath.replace(/"/g, '\\"')}" >nul
,
del "${filepath.replace(/"/g, '\\"')}.b64"
,
del "${filepath.replace(/"/g, '\\"')}.tmp"
,
].join(“\r\n”);
}
async function main() {
console.log(构建开始
);
let packed = [
‘@echo off’,
];
console.log( 树形扫描展开中
);
let file = ‘’;
let tf = ;
while (file = args.files.shift()) {
if (fs.statSync(file).isDirectory()) {
let n_files = await readDirectoryRecursively(file) || ;
let n_dirs = await readDirectory(file) || ;
console.log([
已展开:
,
叶子目录:
,
${n_dirs.join('\n ')}
,
文件:
,
${n_files.join('\n ')}
,
].join(“\n”));
tf = [...tf, ...n_files];
args.dir = [...(args.dir || []), ...n_dirs];
} else tf.push(file);
};
if (args.dir) packed.push(md ${args.dir.map((d) =>
“${d.replace(/”/g, ‘\"’)}").join(" ")}
);
console.log( 构建文件中
);
while (file = tf.shift()) {
let data = await makeBase64(file);
packed.push(data);
};
if (args.run) {
args.run.forEach((r) => {
packed.push(start "" "${r.replace(/"/g, '\\"')}"
);
});
}
if (args.del) packed.push(del "%~0"
);
console.log( 正在保存 ${args.output[0]}
);
fs.writeFileSync(args.output[0], packed.join(“\r\n”), { encoding: ‘latin1’ });
console.log(构建完成
);
}
main();
```
是的,现在用了微软Windows自带的 cap 工具压缩了打包后文件的大小,而且是windows自带
也是不需要再安装什么新东西
然后帮助我写js代码里了,这样子方便看