如何获取 Dart 包中包含的资产的文件路径?

How to get the file path to an asset included in a Dart package?

我正在写一个 Dart 包(不是 Flutter)。我已经包含了一些位图图像作为 public 资产,例如 lib/assets/empty.png。当此包 运行 作为最终用户的命令行应用程序时,我如何获取用户系统上这些资产的文件路径?

用例:我的 Dart 包调用 FFMPEG,我需要告诉 FFMPEG 在使用我的包的系统上哪里可以找到这些资产文件。例如,对 FFMPEG 的调用可能如下所示:

ffmpeg -i "path/to/lib/assets/empty.png" ...

可以通过两种方式访问​​ Dart 包的资产:

  1. 运行 带有 dart 工具并访问依赖项资产的 Dart CLI 应用程序,或
  2. 运行 一个可执行的 CLI 应用程序

这两种情况的区别在于,当您 运行 使用 dart 工具的 CLI 应用程序时,您的所有依赖项都可以作为系统本地缓存中的结构化包使用.但是,当您 运行 一个可执行文件时,所有相关代码都被编译成一个二进制文件,这意味着您无法在运行时访问依赖项的包,您只能访问依赖项的 tree-shaken, 编译代码.

在 运行 dart

时访问资产

以下代码会将包资产 URI 解析为文件系统路径。

final packageUri = Uri.parse('package:your_package/your/asset/path/some_file.whatever');
final future = Isolate.resolvePackageUri(packageUri);

// waitFor is strongly discouraged in general, but it is accepted as the
// only reasonable way to load package assets outside of Flutter.
// ignore: deprecated_member_use
final absoluteUri = waitFor(future, timeout: const Duration(seconds: 5));

final file = File.fromUri(absoluteUri);
if (file.existsSync()) {
  return file.path;
}

此解析代码改编自 Tim Sneath 的 winmd 包:https://github.com/timsneath/winmd/blob/main/lib/src/metadatastore.dart#L84-L106

在 运行 可执行文件

时访问资产

将客户端应用程序编译为可执行文件时,该客户端应用程序根本无法访问与依赖包一起存储的任何资产文件。但是,有一种解决方法可能对某些人有用(它对我有用)。您可以将资产的 Base64 编码版本存储在包内的 Dart 代码中。

首先,将您的每个资产编码成一个 Base64 字符串,并将这些字符串存储在您的 Dart 代码中的某个位置。

const myAsset = "iVBORw0KGgoAAA....kJggg==";

然后,在运行时,将字符串解码回字节,然后将这些字节写入本地文件系统上的新文件。这是我在我的案例中使用的方法:

/// Writes this asset to a new file on the host's file system.
///
/// The file is written to [destinationDirectory], or the current
/// working directory, if no destination is provided.
String inflateToLocalFile([Directory? destinationDirectory]) {
  final directory = destinationDirectory ?? Directory.current;   
  final file = File(directory.path + Platform.pathSeparator + fileName);

  file.createSync(recursive: true);
  final decodedBytes = base64Decode(base64encoded);
  file.writeAsBytesSync(decodedBytes);

  return file.path;
}

此方法由 @passsy

建议