ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NuGet缓存清理与迁移指南:拯救C盘空间的一劳永逸方案

NuGet缓存清理与迁移指南:拯救C盘空间的一劳永逸方案 遇到过Visual Studio的NuGet缓存把C盘塞爆的朋友应该都懂那种看着磁盘空间一点点变红却不知道从哪里下手的烦躁。我自己的电脑就曾经被.nuget文件夹吃掉了将近40GBC盘直接亮红灯后来花了一晚上把缓存迁移到D盘才算根治。这篇就专门聊聊NuGet缓存这回事为什么它这么能吃空间、怎么安全清理、以及最关键的——怎么把缓存目录整体挪走一劳永逸。这篇文章适合所有用Visual Studio做.NET开发的人不管你是用.NET Framework老项目还是.NET 6/8/9的新项目NuGet缓存都一样会长在你的用户目录下面。文章提供的方法我自己实测过也帮几个同事处理过同样的问题按步骤来基本不会翻车。1. NuGet缓存为什么会变成“空间杀手”1.1 先找到缓存到底在哪个位置很多人听说“NuGet缓存占用大”第一反应是去VS的选项里找设置结果翻半天也没找到。原因很简单NuGet缓存默认路径不在Visual Studio的安装目录也不在项目文件夹里而是在当前用户目录下。具体路径是C:\Users\{你的用户名}\.nuget\packages这个文件夹就是NuGet的“全局包文件夹”global-packages folder。你每创建一个项目、每执行一次dotnet restore、每在VS里点一次“还原NuGet程序包”所有下载的包都会被解压到这个地方。还有一个隐藏目录也值得注意C:\Users\{你的用户名}\AppData\Local\NuGet\Cache这个文件夹专门存.nupkg原始安装包文件是NuGet下载时的中间缓存。两个文件夹加起来就是C盘空间的主要消耗来源。1.2 缓存膨胀的根本原因NuGet缓存之所以会越滚越大核心原因有三个第一个原因是版本不清理。NuGet的包是按“包名/版本号”的目录结构存储的。你项目里用过Newtonsoft.Json 12.0.1后来又用了13.0.1最后升级到13.0.3——这三个版本的包会同时躺在磁盘上。除非你手动删除否则旧版本永远不会自动消失。第二个原因是包里的小文件数量惊人。一个NuGet包里可能包含几十个甚至几百个文件lib目录下的DLL、ref目录下的引用程序集、build目录下的构建脚本、content目录下的内容文件……每个包解压后都有一堆文件。文件数量大了以后占用空间倒是其次磁盘碎片和文件索引的负担也在累积。第三个原因是项目间共享但不复用。NuGet设计上确实做了全局共享——同一个包同一个版本只存一份所有项目都引用这一份。听起来很省空间对吧但实际上不同项目引用的包集合差异很大项目多了以后几百个包堆在一起总量一样非常可观。我用PowerShell统计过自己电脑上这个文件夹的情况结果让人吓一跳(Get-ChildItem C:\Users\admin\.nuget\packages -Recurse -File | Measure-Object -Property Length -Sum).Sum / 1GB输出结果37.6 GB。这就是C盘告急的罪魁祸首。2. 先别急着清花两分钟把空间账算清楚2.1 查看NuGet缓存究竟占了多少空间直接清理之前建议先准确摸清空间占用情况。修改路径的风险不大但“不知道删了什么”才是最大的风险。最直观的办法是用可视化磁盘分析工具比如WizTree或者TreeSize。这类软件会按文件夹把磁盘占用画出来一眼就能看到.nuget在哪、占了多少。如果你不想装额外软件用PowerShell也可以快速搞定。统计全局包文件夹大小$size (Get-ChildItem $env:USERPROFILE\.nuget\packages -Recurse -File | Measure-Object -Property Length -Sum).Sum / 1GB Write-Host (NuGet 全局包大小: {0:N2} GB -f $size)注意第一次运行这种递归统计会比较慢文件数量多的时候可能要等一两分钟。别以为卡死了耐心等着就行。2.2 判断哪些缓存能删、哪些不能删搞清楚总量之后还有一个更关键的问题哪些包是正在被使用的哪些是可以用不上的旧版本最简单的判断方法是按日期排序。NuGet包的文件夹结构是包名\版本号比如.nuget\packages\newtonsoft.json\12.0.1 .nuget\packages\newtonsoft.json\13.0.1 .nuget\packages\newtonsoft.json\13.0.3看文件夹的“修改日期”属性长期不更新的版本大概率不是活跃项目在用的。但这里要小心如果你手头有老项目需要维护人家用的就是老版本删掉之后下次打开老项目还是要重新下载。我的判断标准是这样的如果公司网络好、NuGet源访问快直接清掉所有缓存重新还原也无所谓。如果经常在离线环境或者公司内网工作最好保留当前正在开发的几个项目的版本只清理那些明显过时且N久没动过的包。2.3 到底该清理还是该迁移看完数据之后先做一个决策单纯清理不够还是直接迁移我给出一个判断框架方案适合的场景代价只清理不迁移C盘本身空间充足只是偶尔满了清一次过段时间又会满治标不治本清理 迁移C盘空间紧张或者C盘是SSD但容量只有256GB/512GB一次性配置以后一劳永逸迁移但不清理无法重新下载包离线环境迁移耗时很长旧数据全搬过去绝大多数个人开发者直接选择“清理 迁移”两步走就可以。先清理掉无用缓存释放空间再改配置把缓存路径永久指向D盘或其他大容量分区之后新下载的包就不会再碰C盘了。3. 安全清理缓存的三条路3.1 最简单的官方命令nuget locals如果你用的Visual Studio版本比较新2017以上系统自带的NuGet命令就提供了清理入口。打开命令行工具然后执行nuget locals all -clear这条命令会清空全局包文件夹、HTTP缓存文件夹、临时文件夹三个位置的所有内容。如果你只想清其中一部分可以用nuget locals global-packages -clear nuget locals http-cache -clear nuget locals temp -clear这个方法够简单但有个“副作用”所有包的本地副本都会消失。清理完之后你打开的每一个项目第一次还原时都需要重新下载依赖包大项目可能要花好几分钟甚至更久。3.2 精确清理只删旧版本包很多时候你其实不想清空全部缓存只想删掉那些占地方的旧版本。这个就得靠手工加判断了。我的做法是先用PowerShell列出所有旧版本的包文件夹人工扫一眼哪些可以删Get-ChildItem $env:USERPROFILE\.nuget\packages -Directory | ForEach-Object { $pkg $_ $versions Get-ChildItem $pkg.FullName -Directory if ($versions.Count -gt 1) { [PSCustomObject]{ Package $pkg.Name Versions $versions.Count TotalMB [math]::Round(($versions | Get-ChildItem -Recurse -File | Measure-Object Length -Sum).Sum / 1MB, 2) } } } | Sort-Object TotalMB -Descending | Format-Table -AutoSize这条命令会列出所有拥有多个版本的包以及每个包的总占用大小。输出结果里你就能看出来哪些包是“版本大户”比如Microsoft.AspNetCore.App、System.Text.Json这些动辄好几个版本。确认好要删哪些版本之后直接在文件资源管理器里把对应目录删除就行。比如要删掉System.Text.Json的4.7.0版本假设你现在的项目都在用更高版本C:\Users\admin\.nuget\packages\system.text.json\4.7.0删掉这个文件夹即可不影响任何正在运行的项目。下一次有项目还引用这个版本时NuGet会重新下载它。3.3 用磁盘分析工具辅助排查文件一多光靠命令行列目录还是费劲。这种时候用图形化工具效率高得多。WizTree是我实测最快的一个扫描整个C盘只要几秒钟。扫描完成之后按文件大小排序一眼就能定位.nuget下面到底哪些包最占空间。它的“按扩展名汇总”功能也很有用。NuGet包里大量的小型JSON文件、XML文件、PNG图片单看不显眼但加起来占用非常可观。在WizTree里选定.nuget文件夹看文件类型分布基本能直观理解“为什么这玩意儿这么占空间”。3.4 清理后的注意事项清理缓存这个动作做完之后有几件事需要立刻确认打开一个项目试试还原是否正常。找一个依赖包比较全的项目在VS里右键解决方案选“还原NuGet程序包”。如果还原过程没有报错说明一切正常。检查离线包源是否受影响。如果你平时用dotnet发布时指定了--source指向本地文件夹作为包源清理全局缓存不会影响那个文件夹里的.nupkg文件。别手滑清理了不该清的目录。.nuget文件夹里除了packages目录还有一个plugins目录这个存放NuGet插件的目录不要动。清理掉可能导致后续一些扩展命令失效比如某些私有NuGet源的身份验证插件。注意nuget locals all -clear会同时清理global-packages、http-cache和temp三个位置。在命令执行后Windows的“存储感知”功能并不会自动帮你识别.nuget文件夹的内容所以别指望系统帮你清得手动做。4. 一劳永逸把缓存目录迁出C盘4.1 为什么迁移比清理更值得做清理缓存的问题在于每次清理都意味着下次打开项目要重新下载包。如果你做的是ASP.NET Core这类依赖较多的大型项目一次还原可能要下载几百MB甚至几个GB的包耗时不说还容易在下载过程中遇到断网、超时等问题。把缓存目录整体迁移到D盘等于是“一劳永逸”C盘空间保住了包缓存还在还原速度和以前一样快。唯一的成本就是第一次迁移时要把已有的缓存文件复制过去或者干脆重新下载。而且NuGet本身对缓存路径的可配置性做得很好官方支持通过环境变量NUGET_PACKAGES指定全局包文件夹路径。Visual Studio也会读取这个环境变量。也就是说你不需要改任何项目代码或配置文件只要设置好环境变量全局生效。4.2 迁移步骤详解整个迁移过程分四步我按自己的实际操作顺序写出来第一步创建目标文件夹在D盘或者其他空间充足的分区建一个专门放NuGet缓存的目录比如D:\NuGet\Packages注意路径里不要用中文和空格省得某些老工具的兼容性出问题。第二步设置环境变量打开“系统属性 → 环境变量”新建一个用户变量变量名: NUGET_PACKAGES 变量值: D:\NuGet\Packages保存之后后续所有通过dotnet restore、VS还原NuGet包的操作都会把包缓存写到这个新路径。第三步复制旧缓存可选如果你不想重新下载所有依赖包可以把原来C:\Users\{用户名}\.nuget\packages文件夹里的内容原样复制到新目录。文件数量多的话复制过程会有点慢建议用robocopy来复制稳定而且速度快robocopy C:\Users\admin\.nuget\packages D:\NuGet\Packages /E /COPYALL /DCOPY:DAT第四步确认新路径生效设置完环境变量后重新打开一个命令行窗口执行下面的命令看NuGet识别到的新路径是什么dotnet nuget locals global-packages --list正常情况下输出应该显示info : 全局包文件夹: D:\NuGet\Packages看到这个结果就说明新路径已经生效。之后你还原任何项目包都会写到D盘C盘的.nuget文件夹不会再增长了。4.3 迁移后旧缓存的清理新路径生效之后原来C盘里的.nuget\packages文件夹就成了“历史遗留物”。确认以下几点后可以放心删除所有解决方案的“还原NuGet程序包”都验证过没有问题robocopy复制过程没有报错或者你已经做好重新下载的准备了各个项目的obj\project.assets.json文件已经能正常生成删除时的建议操作是在文件资源管理器里把这个文件夹改名比如改成.nuget_packages_old然后用几天看看有没有任何项目报错。没问题再彻底删除。这种方法虽然多占几天空间但最大程度保证了安全性。4.4 还有两个容易遗漏的缓存位置迁移完global-packages之后C盘上可能还留着两处NuGet相关的缓存第一处是C:\Users\{用户名}\AppData\Local\NuGet\Cache这个目录存放NuGet的HTTP下载缓存。虽然不算特别大但日积月累也能到几个GB。可以通过取消勾选VS里的“允许NuGet下载缺失的包”相关选项来减少增长或者定期执行nuget locals http-cache -clear来清理。第二处是C:\Users\{用户名}\.nuget\packages\.tools目录如果存在。这个目录存放的是NuGet工具包一般占用不大但如果它吃了几百MB也不意外。迁移时如果robocopy全量复制了.nuget\packages文件夹这个目录也会跟着过去不用单独处理。如果你还想再省一点C盘空间可以顺手把VS自身的组件缓存也检查一下。比如“VS的安装缓存”、“组件下载缓存”等但那些不影响项目运行属于锦上添花的事不在这里展开。5. 迁移后常见的坑与排查方法5.1 VS仍然显示使用旧路径是怎么回事这是问得最多的一个问题。明明设置了NUGET_PACKAGES环境变量但VS里打开“工具 → NuGet包管理器 → 程序包管理器设置”看到的还是C:\Users\{用户名}\.nuget\packages。原因在于VS的这个设置页面优先读取的是NuGet.Config配置文件里config节中对globalPackagesFolder的设置。如果项目根目录或者用户目录下的NuGet.Config中存在显式配置它就会覆盖环境变量。解决办法也不复杂。检查用户目录下的配置文件C:\Users\{用户名}\AppData\Roaming\NuGet\NuGet.Config在config节中添加或修改这一行add keyglobalPackagesFolder valueD:\NuGet\Packages /保存后重启VS再查看设置页就会显示新路径了。环境变量和配置文件同时生效时配置文件优先。5.2 清理缓存后项目还原报错这种问题通常发生在清理缓存之后重新还原项目时提示NU1102: 找不到版本为 6.0.0 的 System.Text.Json 包其实不是包缺失而是NuGet源拉取超时或者找错了源。排查顺序是这样检查NuGet包源Package Source列表是否包含官方源nuget.org或者你公司内网源。检查网络代理设置是否正确。执行一次dotnet restore --force强制重新评估所有依赖项。大多数情况下把源指对了就能解决。遇到公司内网源不稳定的情况我在本地建了一个私有源镜像专门保存常用的包这样即便清理了全部缓存也能快速还原。5.3 迁移之后某些老项目仍然往C盘写缓存这个问题比较隐蔽。有些老项目尤其是.NET Framework时代的项目使用packages.config管理NuGet包它们的行为和新式“PackageReference”项目不太一样包会直接还原到解决方案根目录下的packages文件夹里而不会放到全局包文件夹。也就是说即使你迁移了全局缓存路径老项目每次还原时还是会把包下载到自己项目下面的packages目录。如果这些项目都在C盘那C盘空间还是会减少。处理思路是给这些老项目也改一条出路把整个解决方案都放在D盘比如D:\Projects\LegacyApp这样packages目录就落在D盘了。或者如果你确定老项目不再维护也不需要反复还原直接在磁盘上删掉它们的packages文件夹也可以等真要用的时候再还原。5.4 误删了缓存还能补救吗答案是可以。.nuget缓存不属于唯一数据源真正唯一的源是NuGet仓库。即使你把整个.nuget\packages文件夹都删了只要你还能访问NuGet源互联网或者内网重新执行一次还原就能下载回来。唯一需要担心的场景是你完全离线且手动下载过一些私有包来自私有NuGet源但那个源已经不可访问了。这种情况就比较棘手所以在清理之前建议把项目用到的所有包列表导出一份。导出当前项目引用的所有包dotnet list yourproject.csproj package --include-transitive这样万一以后需要离线还原还能对着清单去别的机器上手动拷贝。6. 我的最终建议我自己电脑上最后采用的是“清理旧缓存 环境变量迁移 配置文件锁死路径”三件套组合拳。实际执行完之后C盘从只剩8GB空间恢复到剩75GB而且之后连续开发了三个月C盘占用一直很稳定没有再出现空间告急的情况。如果你也想照着做我建议的顺序是先清一波旧版本缓存第3.2节的方法看看能释放多少空间然后直接迁移目录第4.2节新下载的包落到D盘最后把旧文件夹改名留几天做验证确认无误后删除。最后提个醒迁移完之后顺手检查一下有没有项目使用了硬编码的NuGet缓存路径。我遇到过有同事在Directory.Build.props里写死了RestorePackagesPath导致怎么改环境变量都不生效卡了半天。遇到这种情况去项目文件或者Directory.Build.props里搜一下“RestorePackagesPath”关键词改掉或者删除重试就行。
返回列表