About using pkg to package the node project into an exe executable file, which contains instructions for locating the problem that the command line execution code cannot be executed

Recently, a friend asked me a question, why after he packaged the node project into an exe file, his project did not run normally, that is, he needed to play a piece of audio. His code is as follows

Project engineering code view

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/");
});

We can see that there is nothing wrong with this code. It is nothing more than an http service with port 8000. When a user accesses this service interface, a message will be sent to the client, and the audio playback operation will be performed on the backend.

Then let's see if there is any problem with packaging pkg into an executable exe command. By looking at package.json , we can see the complete configuration

{
    
    
  "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"
  }
}

Roughly from here, we also see that the pkg command parameter is an exe file compiled with node16-win-x64 as the target environment, but it is amazing that the audio is not played when it seems to be fine.

Sound-play library source code analysis (note that I added logs here for easy positioning)

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;
        }
    },
};

From the above code, we know that the core principle here is to use powershell to call the player of the system to play audio. Here we know the reason why it cannot be called, because there is a security policy problem when an exe file calls powershell, so naturally it cannot be normal transfer.

Change the way of thinking, since this does not work, the easiest way for us is to write a bat script, which is also a double-click operation

start cmd /k "cd  node完整工程目录 && npm run start"

The above is a simple analysis. In fact, during the analysis process, the printing of the log is also quite critical. In the above troubleshooting, in fact, in addition to finding that powershell cannot be called, there is another problem that when we package it into an exe executable file, Then the path obtained by __dirname is virtual, which makes it impossible to call even if we let go of the security policy, because the virtual path cannot be accessed in powershell. At this time, we can use the relative path method, that is, path.join(__dirname, "./dist/alert.mp3")change the method to path.resolve("./dist/alert.mp3"); the relative path is enough.

Guess you like

Origin blog.csdn.net/weixin_39370315/article/details/129959633