使用 nuget.packaging ≥ 6.0 可稳定创建 .nupkg,需用 packagebuilder 显式添加文件(路径用 /)、构造元数据,解包须用 packagereader 或 zipfile;符号包需严格遵循 src/ 结构,跨平台注意路径分隔符与编码。

用 NuGet.Packaging 创建 .nupkg 文件
直接调用 NuGet.Packaging 是目前最稳定、可控的方式,比旧版 NuGet.Core 或命令行更适配现代 SDK 风格项目。它不依赖 MSBuild,适合 CI 脚本或工具链集成。
常见错误是误用 NuGet.Packaging.Core(已废弃)或混淆 NuspecReader 和 PackageBuilder 的职责——前者只读元数据,后者才真正打包。
- 安装包:
NuGet.Packaging≥ 6.0(.NET 6+ 推荐 6.10+,修复了符号包路径 bug) - 必须先生成
.nuspec或用Manifest对象构造元数据;不能仅靠文件夹结构自动推断 - 内容文件需显式添加:用
builder.AddFiles(...),路径要相对于包根(如lib/net6.0/MyLib.dll),不是绝对路径 - 若含
tools/或build/目录,权限位不影响 Windows,但 Linux 下解包可能丢执行位——实际无影响,NuGet 不校验
var builder = new PackageBuilder();
builder.Id = "My.Package";
builder.Version = new NuGetVersion("1.0.0");
builder.Authors.Add("me");
builder.Description = "A demo";
builder.AddFile("bin/Release/MyLib.dll", "lib/net6.0/MyLib.dll");
using var nupkgStream = File.Create("My.Package.1.0.0.nupkg");
builder.Save(nupkgStream);
用 dotnet pack 解包 .nupkg 文件
dotnet pack 只能打包,不能解包。想“解包”,得用 NuGet.Packaging 的 PackageReader,或者临时解压——因为 .nupkg 就是 ZIP,但直接改后缀解压会丢签名(_rels、[Content_Types].xml 等元数据仍存在,不影响内容读取)。
容易踩的坑是以为 nuget.exe 的 unpack 命令可用:它从 5.11 起已被移除,且不支持 v3 协议格式的包(即大多数新包)。
- 推荐做法:用
ZipFile.ExtractToDirectory快速查看内容,适用于调试或验证结构 - 若需读取元数据(如依赖项、作者、描述),必须用
PackageReader,否则解析.nuspec容易漏掉自动生成的字段(如<dependencies></dependencies>来自PackageReference) - 签名验证失败时(
NuGet.Protocol.Core.Types.FatalProtocolException),不是包损坏,而是你用了带签名的包但没引用NuGet.Signing,跳过验证即可
using var stream = File.OpenRead("My.Package.1.0.0.nupkg");
using var reader = new PackageReader(stream);
var metadata = reader.NuspecReader.GetMinimalPackageMetadata();
Console.WriteLine(metadata.Id); // My.Package
处理带符号的 .nupkg(.snupkg)
符号包不是独立格式,只是普通 .nupkg 加了 symbols 标识和特定目录结构(src/、symbol/)。创建时别手动塞 .pdb 到任意路径——必须进 src/ 并保留原始项目结构,否则调试器找不到源码。
常见误解是认为 dotnet pack --include-symbols 会自动处理所有符号:它只打包当前项目的 .pdb,不递归打包 ProjectReference 的符号,那些得单独打包并发布到符号服务器。
- 用
PackageBuilder手动加符号:路径必须以src/开头,例如src/MyLib/Program.cs - 符号包文件名必须匹配主包名 +
.snupkg,且Id和Version完全一致,否则 NuGet.Server 拒绝索引 - 本地调试时,VS 默认不查本地文件夹里的 .snupkg——得在选项里启用“仅我的代码”关闭,并添加符号路径到
Tools → Options → Debugging → Symbols
跨平台兼容性与路径陷阱
Windows 上用反斜杠 构造包内路径(如 lib
et6.0My.dll)会导致 Linux/macOS 下读取失败:ZIP 规范强制使用正斜杠 /,NuGet.Packaging 内部会标准化,但手动拼接时若混用,AddFiles 可能静默跳过文件。
另一个隐形问题是长路径:.nupkg 内路径总长度超 260 字符时,Windows 下 ZipFile.CreateFromDirectory 会报错,但 PackageBuilder 不会——它走的是内存流,不经过文件系统路径检查。
- 所有包内路径一律用
/,用Path.Replace("\", "/")不可靠,应统一用Path.GetRelativePath+.Replace(Path.DirectorySeparatorChar, '/') - 避免在
id或version中用非法字符(如/、),NuGet 会抛 <code>ArgumentException,但错误信息只说“无效的包标识符”,不指明哪部分 - Linux 容器里运行打包代码时,若宿主机挂载路径含空格或中文,
File.OpenRead可能失败——不是编码问题,是 .NET 6+ 对 URI 解码更严格,建议用Uri.EscapeDataString处理路径
/ 或多一个 src/ 层级,下游就拉不到依赖。










