---
title: 嵌入外部二进制文件
i18nReady: true
---

您可能需要嵌入外部二进制文件来为应用程序添加额外的功能，或者防止用户安装其他依赖项（例如 Node.js 或 Python）。我们将这个二进制文件称为 `sidecar`。

二进制文件是用任何编程语言编写的可执行文件。常见的用例是使用 `pyinstaller` 捆绑的 Python CLI 应用程序或 API 服务器。

要捆绑您选择的二进制文件，您可以在 `tauri.conf.json` 中的 `tauri > bundle` 对象中添加 `externalBin` 属性。
`externalBin` 期望一个字符串列表，用于定位具有绝对或相对路径的二进制文件。

下面是一个示例来说明配置。这不是一个完整的 `tauri.conf.json` 文件。

```json title="src-tauri/tauri.conf.json"
{
  "bundle": {
    "externalBin": [
      "/absolute/path/to/sidecar",
      "relative/path/to/binary",
      "binaries/my-sidecar"
    ]
  }
}
```

:::note
相对路径是相对于 `tauri.conf.json` 文件的，该文件位于 `src-tauri` 目录中。因此，`binaries/my-sidecar` 表示的是 `<项目根目录>/src-tauri/binaries/my-sidecar`。
:::

为了使外部二进制文件在每个支持的架构上工作，指定路径上必须存在一个具有相同名称和 `-$TARGET_TRIPLE` 后缀的二进制文件。例如，`"externalBin": ["binaries/my-sidecar"]` 需要在Linux上有一个名为 `src-tauri/binaries/my-sidecar-x86_64-unknown-linux-gnu` 的可执行文件，或者在 Mac OS with Apple Silicon 上有一个名为 `src-tauri/binaries/my-sidecar-aarch64-apple-darwin` 的可执行文件。

你可以通过查看 `rustc -Vv` 命令报告的 `host:` 属性来找到当前平台的 `-$TARGET_TRIPLE` 后缀。

如果大多数 Unix 系统都有 `grep` 和 `cut` 命令，则可以使用以下命令直接提取 `$TARGET_TRIPLE`。

```shell
rustc -Vv | grep host | cut -f2 -d' '
```

在 Windows 上，你可以使用 PowerShell 代替。

```powershell
rustc -Vv | Select-String "host:" | ForEach-Object {$_.Line.split(" ")[1]}
```

下面是一个将 `$TARGET_TRIPLE` 追加到二进制文件的 Node.js 脚本:

```javascript
const execa = require('execa');
const fs = require('fs');

let extension = '';
if (process.platform === 'win32') {
  extension = '.exe';
}

async function main() {
  const rustInfo = (await execa('rustc', ['-vV'])).stdout;
  const targetTriple = /host: (\S+)/g.exec(rustInfo)[1];
  if (!targetTriple) {
    console.error('Failed to determine platform target triple');
  }
  fs.renameSync(
    `src-tauri/binaries/sidecar${extension}`,
    `src-tauri/binaries/sidecar-${targetTriple}${extension}`
  );
}

main().catch((e) => {
  throw e;
});
```

注意，如果编译的体系结构与运行的体系结构不同，这个脚本将无法工作。

## 从 Rust 运行

在 Rust 端, 导入 `tauri_plugin_shell::ShellExt` 特征，并在 `AppHandle` 上调用 `shell().sidecar()` 函数。

```rust
use tauri_plugin_shell::ShellExt;
use tauri_plugin_shell::process::CommandEvent;

// `sidecar()` 只需要文件名, 不像 JavaScript 中的整个路径
let sidecar_command = app.shell().sidecar("my-sidecar").unwrap();
let (mut rx, mut _child) = sidecar_command
  .spawn()
  .expect("Failed to spawn sidecar");

tauri::async_runtime::spawn(async move {
  // 读取诸如 stdout 之类的事件
  while let Some(event) = rx.recv().await {
    if let CommandEvent::Stdout(line) = event {
      window
        .emit("message", Some(format!("'{}'", line)))
        .expect("failed to emit event");
      // 写入 stdin
      child.write("message from Rust\n".as_bytes()).unwrap();
    }
  }
});
```

您可以将此代码放在 Tauri 命令中，以便轻松传递 AppHandle，或者您可以在构建脚本中存储对 AppHandle 的引用，以便在应用程序的其他位置访问它。

## 从 JavaScript 运行

在 JavaScript 代码中，从 `@tauri-apps/plugin-shell` 模块中导入 `Command` 类，并使用其静态方法 `sidecar`。

```javascript
import { Command } from '@tauri-apps/plugin-shell';
// `binaries/my-sidecar` 是在 `tauri.conf.json > tauri > bundle > externalBin` 指定的确切值。
const sidecar_command = Command.sidecar('binaries/my-sidecar');
const output = await sidecar_command.execute();
```

## 传递参数

你可以像运行普通的 `Command` 一样向 Sidecar 命令传递参数。

参数可以是**静态的**（例如 `-o` 或 `serve`）或者**动态的**（例如 `<file_path>` 或者 `localhost:<PORT>`）。
你按照调用它们的确切顺序来定义这些参数。静态参数保持不变，而动态参数可以使用正则表达式来定义。

首先，在 `src-tauri/capabilities/default.json` 中定义需要传递给 sidecar 命令的参数。

```json title="src-tauri/capabilities/default.json" ins={14-30}
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "default",
  "description": "Capability for the main window",
  "windows": ["main"],
  "permissions": [
    "core:path:default",
    "core:event:default",
    "core:window:default",
    "core:app:default",
    "core:resources:default",
    "core:menu:default",
    "core:tray:default",
    {
      "identifier": "shell:allow-execute",
      "allow": [
        {
          "args": [
            "arg1",
            "-a",
            "--arg2",
            {
              "validator": "\\S+"
            }
          ],
          "cmd": "",
          "name": "binaries/my-sidecar",
          "sidecar": true
        }
      ]
    }
  ]
}
```

:::note
如果你正在从 Tauri v1 迁移，Tauri v2 CLI 中的 `migrate` 命令应该会为你处理这个问题。阅读[自动迁移](/zh-cn/start/migrate/from-tauri-1/#自动迁移)了解更多信息。
:::

然后，要调用 sidecar 命令，只需将**所有**参数作为数组传入即可。

在 Rust 中：

```rust
use tauri_plugin_shell::ShellExt;
#[tauri::command]
async fn call_my_sidecar(app: tauri::AppHandle) {
  let sidecar_command = app.shell()
    .sidecar("my-sidecar")
    .unwrap()
    .args(["arg1", "-a", "--arg2", "any-string-that-matches-the-validator"]);
  let (mut _rx, mut _child) = sidecar_command.spawn().unwrap();
}
```

在 JavaScript 中：

```javascript
import { Command } from '@tauri-apps/plugin-shell';
// `binaries/my-sidecar` 是在 `tauri.conf.json > tauri > bundle > externalBin` 指定的确切值。
// 请注意 args 数组需要与 `tauri.conf.json` 中指定的需完全匹配
const command = Command.sidecar('binaries/my-sidecar', [
  'arg1',
  '-a',
  '--arg2',
  'any-string-that-matches-the-validator',
]);
const output = await command.execute();
```
