单文件部署
通过将所有依赖应用程序的文件捆绑到一个二进制文件中,为应用程序开发人员提供一个具有吸引力的选项,那就是将应用程序作为单个文件进行部署和分发。 单文件部署可用于依赖框架的部署模型和独立应用程序。
独立应用程序中单个文件的大小很大,因为它包含运行时和框架库。 在 .NET 6 中,可以通过发布剪裁来减小与剪裁兼容的应用程序的总大小。 单文件部署选项可与 ReadyToRun 和 Trim 发布选项结合使用。
重要
要在 Windows 7 上运行单个文件应用,必须使用 .NET 运行时 6.0.3 或更高版本。
示例项目文件
下面是指定单文件发布的示例项目文件:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
</Project>
这些属性具有下列函数:
PublishSingleFile
。 启用单文件发布。 此外,还会在dotnet build
期间启用单文件警告。SelfContained
。 确定应用是独立的还是依赖于框架的。RuntimeIdentifier
。 指定目标 OS 和 CPU 类型。 默认情况下,还会设置<SelfContained>true</SelfContained>
。
单文件应用始终特定于 OS 和体系结构。 需要为每个配置发布,例如 Linux x64、Linux Arm64、Windows x64 等。
单个文件中包含运行时配置文件,例如 *.runtimeconfig.json 和 *.deps.json。 如果需要额外的配置文件,可将其放在单个文件旁边。
发布单文件应用
使用 dotnet publish 命令发布单文件应用程序。
将
<PublishSingleFile>true</PublishSingleFile>
添加到项目文件。此更改将针对独立发布生成一个单文件应用。 它还会在生成期间显示单文件兼容性警告。
<PropertyGroup> <PublishSingleFile>true</PublishSingleFile> </PropertyGroup>
使用
dotnet publish -r <RID>
针对特定运行时标识符发布应用以下示例将 Windows 应用作为独立的单一文件应用程序发布。
dotnet publish -r win-x64
以下示例将 Linux 应用作为依赖框架的单一文件应用程序发布。
dotnet publish -r linux-x64 --self-contained false
应在项目文件中设置 <PublishSingleFile>
以在生成期间启用文件分析,但也可以将这些选项作为 dotnet publish
参数传递:
dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained false
有关详细信息,请参阅使用 .NET CLI 发布 .NET Core 应用。
将文件排除在嵌入之外
通过设置以下元数据,可明确指定不在单文件中嵌入某些文件:
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
例如,若要将某些文件放置在发布目录中,但不将它们捆绑到文件中:
<ItemGroup>
<Content Update="Plugin.dll">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>
在绑定内包括 PDB 文件
可以使用下面的设置将程序集的 PDB 文件嵌入到程序集本身 (.dll
)。 由于符号是程序集的一部分,因此,它们也是应用程序的一部分:
<DebugType>embedded</DebugType>
例如,将以下属性添加到程序集的项目文件,以将 PDB 文件嵌入到该程序集:
<PropertyGroup>
<DebugType>embedded</DebugType>
</PropertyGroup>
其他注意事项
单文件应用程序旁边将具有所有相关的 PDB 文件,并且默认情况下不会绑定。 如果要在生成的项目的程序集内包含 PDB,请将 DebugType
设置为 embedded
。 请查阅在绑定内包括 PDB 文件。
托管的 C++ 应组件不太适合进行单文件部署。 建议使用 C# 或其他非托管 C++ 语言编写应用程序来实现单文件兼容。
本机库
仅将托管的 DLL 与应用捆绑到一个可执行文件中。 当应用启动时,托管的 DLL 将被提取并加载到内存中,从而避免提取到文件夹中。 使用此方法,托管二进制文件将嵌入单文件捆绑包中,但核心运行时本身的本机二进制文件是单独的文件。
若要嵌入这些文件以进行提取并获取一个输出文件,请将属性 IncludeNativeLibrariesForSelfExtract
设置为 true
。
指定 IncludeAllContentForSelfExtract
将在运行可执行文件之前提取所有文件,包括托管程序集。 这可能有助于解决罕见的应用程序兼容性问题。
重要
如果进行提取,则会在应用启动前将文件提取到磁盘:
- 如果
DOTNET_BUNDLE_EXTRACT_BASE_DIR
环境变量设为路径,则会将文件提取到该路径下的目录。 - 此外,如果在 Linux 或 MacOS 上运行,则会将文件提取到
$HOME/.net
下的目录。 - 如果在 Windows 上运行,则会将文件提取到
%TEMP%/.net
下的目录。
若要防止篡改,这些目录不应由具有不同权限的用户或服务写入。 在大多数 Linux 和 MacOS 系统上,不要使用 /tmp 或 /var/tmp。
注意
在某些 Linux 环境下(例如在 systemd
下),由于未定义 $HOME
,无法进行默认提取。 在这种情况下,建议显式设置 $DOTNET_BUNDLE_EXTRACT_BASE_DIR
。
对于 systemd
,还可将服务单元文件中的 DOTNET_BUNDLE_EXTRACT_BASE_DIR
定义为 %h/.net
,systemd
可为运行该服务的帐户将其正确地扩展为 $HOME/.net
。
[Service]
Environment="DOTNET_BUNDLE_EXTRACT_BASE_DIR=%h/.net"
API 不兼容
某些 API 与单文件部署不兼容。 如果应用程序使用这些 API,可能需要进行修改。 如果使用第三方框架或包,则它们可能使用了这样的 API 并需要修改。 出现问题的最常见原因是依赖于应用程序附带的文件或 DLL 的文件路径。
下表提供了用于单文件的相关运行时库 API 详细信息。
API | 注意 |
---|---|
Assembly.CodeBase |
引发 PlatformNotSupportedException。 |
Assembly.EscapedCodeBase |
引发 PlatformNotSupportedException。 |
Assembly.GetFile |
引发 IOException。 |
Assembly.GetFiles |
引发 IOException。 |
Assembly.Location |
返回空字符串。 |
AssemblyName.CodeBase |
返回 null 。 |
AssemblyName.EscapedCodeBase |
返回 null 。 |
Module.FullyQualifiedName |
返回值为 <Unknown> 的字符串,或引发异常。 |
Marshal.GetHINSTANCE |
返回 -1。 |
Module.Name |
返回值为 <Unknown> 的字符串。 |
我们提供了关于修复常见问题的一些建议:
若要访问可执行文件旁边的文件,请使用 AppContext.BaseDirectory。
若要查找可执行文件的文件名,请使用 Environment.GetCommandLineArgs() 的第一个元素;从 .NET 6 开始,请使用 ProcessPath 中的文件名。
若要避免完全发送松散文件,请考虑使用嵌入的资源。
捆绑前对二进制文件进行后处理
某些工作流需要在捆绑之前对二进制文件进行后处理。 一个常见示例是签名。 dotnet SDK 提供 MSBuild 扩展点,以允许在单文件捆绑之前处理二进制文件。 可用 API 包括:
- 将在
GenerateSingleFileBundle
之前调用的目标PrepareForBundle
- 包含将捆绑的所有文件的
<ItemGroup><FilesToBundle /></ItemGroup>
- 将指定 apphost 模板的属性
AppHostFile
。 后处理可能需要将 apphost 从处理中排除。
若要插入此内容,需要创建将在 PrepareForBundle
和 GenerateSingleFileBundle
之间执行的目标。
请考虑以下 .NET 项目 Target
节点示例:
<Target Name="MySignedBundledFile" BeforeTargets="GenerateSingleFileBundle" DependsOnTargets="PrepareForBundle">
工具可能需要在签名过程中复制文件。 如果原始文件是不属于生成的共享项(例如,该文件来自 NuGet 缓存),则可能会发生这种情况。 在这种情况下,工具预期会修改相应 FilesToBundle
项的路径,以指向修改后的副本。
在单文件应用中压缩程序集
在嵌入式程序集上启用压缩后,可以创建单文件应用。 将 EnableCompressionInSingleFile
属性设置为 true
。 生成的单个文件将包含所有已压缩的嵌入式程序集,这可以显著减小可执行文件的大小。
压缩会导致性能下降。 在应用程序启动时,必须将程序集解压缩到内存中,这需要花费一些时间。 在使用压缩之前,建议衡量启用压缩造成的大小更改和启动开销影响。 这种影响在不同的应用程序之间有很大的差异。
检查单文件应用
可以使用 ILSpy 工具检查单文件应用。 该工具可以显示捆绑到应用程序中的所有文件,并且可以检查托管程序集的内容。