发布:2023/11/12 13:15:43作者:管理员 来源:本站 浏览次数:663
近期有朋友请教我一个问题,为何他将node项目打包成exe文件之后,他的项目就运行不正常了,也就是需要播放一段音频。他的代码如下
项目工程代码查看
const http = require("http");
const sound = require("sound-play");
const path = require("path");
const fs = require("fs");
const filePath = path.join(__dirname, "./dist/alert.mp3");
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain;charset=utf-8" });
res.end("测试音频播放\n");
sound.play(filePath);
});
server.listen(8000, () => {
console.log("Server running at http://localhost:8000/");
});
这段代码我们看下来也没啥问题,无非就是起了一个端口为8000的http服务,当用户访问这个服务接口时,就会发送消息给客户端,同时后端进行音频播放操作。
在然后我们看下pkg 打包成可执行exe 命令有没有问题,通过查看 package.json,我们看下完整配置
{
"name": "alert",
"bin": "./index.js",
"scripts": {
"pkg": "pkg . -d -t node16-win-x64 -o",
"start": "node index.js"
},
"pkg": {
"assets": [
"dist/**/*"
]
},
"dependencies": {
"sound-play": "^1.1.0"
}
}
大致从这里我们也看到了 pkg 命令参数,是以 node16-win-x64 为目标环境编译的exe 文件,但是很神奇,看似没问题的地方,运行起来,音频就是没有播放。
sound-play 库源码分析(注意这里是我添加了日志,方便定位的)
const { exec } = require("child_process");
const fs = require("fs");
const execPromise = require("util").promisify(exec);
/* MAC PLAY COMMAND */
const macPlayCommand = (path, volume) => `afplay \"${path}\" -v ${volume}`;
/* WINDOW PLAY COMMANDS */
const addPresentationCore = `Add-Type -AssemblyName presentationCore;`;
const createMediaPlayer = `$player = New-Object system.windows.media.mediaplayer;`;
const loadAudioFile = (path) => `$player.open('${path}');`;
const playAudio = `$player.Play();`;
const stopAudio = `Start-Sleep 1; Start-Sleep -s $player.NaturalDuration.TimeSpan.TotalSeconds;Exit;`;
const windowPlayCommand = (path, volume) =>
`powershell -c ${addPresentationCore} ${createMediaPlayer} ${loadAudioFile(
path
)} $player.Volume = ${volume}; ${playAudio} ${stopAudio}`;
module.exports = {
play: async (path, volume = 0.5) => {
/**
* Window: mediaplayer's volume is from 0 to 1, default is 0.5
* Mac: afplay's volume is from 0 to 255, default is 1. However, volume > 2 usually result in distortion.
* Therefore, it is better to limit the volume on Mac, and set a common scale of 0 to 1 for simplicity
*/
try {
const volumeAdjustedByOS =
process.platform === "darwin"
? Math.min(2, volume * 2)
: volume;
const playCommand =
process.platform === "darwin"
? macPlayCommand(path, volumeAdjustedByOS)
: windowPlayCommand(path, volumeAdjustedByOS);
fs.appendFile(
"./log.txt",
`${new Date().toLocaleString()},${playCommand}\n`,
(error) => {}
);
fs.appendFile(
"./log.txt",
`${new Date().toLocaleString()},开始音频播放\n`,
(error) => {}
);
await execPromise(playCommand);
} catch (err) {
fs.appendFile(
"./log.txt",
`${new Date().toLocaleString()},${err}\n`,
(error) => {}
);
throw err;
}
},
};
从上述代码,我们知道这里的核心原理便是利用powershell 调用系统的播放器进行音频的播放,这里我们就知道不能调用的原因了,因为一个exe文件调用powershell 是有安全策略问题的, 自然不能正常调用。
转换思路,既然这个行不通,那我们最简单的方式,可以写bat脚本呀,也是双击操作
start cmd /k "cd node完整工程目录 && npm run start"
以上就是一段简单的分析了,其实分析过程中,日志的打印也是蛮关键的,我在上述排除问题中,其实除了发现是powershell 无法调用,还有一个就是当我们打包成exe可执行文件之后,那么__dirname 获取到的路径是虚拟的,这也导致即使我们放开了安全策略,也会无法调用,因为虚拟路径在powershell 中是访问不到的。这个时候我们可以采用相对路径的方式 即把 path.join(__dirname, "./dist/alert.mp3") 方法 改成path.resolve("./dist/alert.mp3");相对路径即可。
© Copyright 2014 - 2024 柏港建站平台 ejk5.com. 渝ICP备16000791号-4