前言
开发人员的 C 盘经常会被各种 SDK、缓存和依赖包塞满。Android SDK、npm 全局包、Docker 镜像、Gradle 缓存等文件夹动辄占用几十 GB 空间。直接删除会导致开发工具无法正常工作,手动修改每个工具的配置路径又繁琐且容易遗漏。
使用 Windows 的目录联接(Directory Junction)功能,可以在不改变原路径的情况下,将文件夹的实际数据存储在其他盘。
这种方法对开发工具完全透明,工具仍然访问原来的路径,但数据实际存储在目标盘。
本文将介绍如何安全地迁移常见的开发环境文件夹,包括 Android SDK、npm 缓存、Gradle 缓存等。
原理说明
Windows 提供了 mklink /J 命令来创建目录联接。
目录联接类似于符号链接,它会在原路径创建一个”快捷方式”,指向新的实际存储位置。
当程序访问原路径时,系统会自动重定向到目标路径,程序本身无需任何修改。
这种方式的优势在于对应用程序完全透明,不需要修改任何环境变量或配置文件。
迁移前的准备工作
在开始迁移之前,需要完成以下准备工作。
关闭所有可能使用目标文件夹的程序,包括 IDE、终端、后台服务等。
确认目标盘有足够的可用空间,建议预留比原文件夹大 10% 的空间。
以管理员身份打开命令提示符或 PowerShell,确保有足够的权限执行操作。
记录原文件夹的完整路径,后续需要创建相同名称的联接点。
实现
查找占用空间的文件夹
首先需要找出哪些开发相关的文件夹占用了大量空间。
使用以下 PowerShell 脚本可以查看指定目录下各子文件夹的大小。
为了方便扫描多个位置(如 AppData\Local 和用户主目录),可以定义一个辅助函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function Show-LargeFolders { param([string]$Path) Get-ChildItem -Path $Path -Directory -Force | ForEach-Object { $size = (Get-ChildItem $_.FullName -Recurse -File -Force -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum [PSCustomObject]@{ Name = $_.Name SizeGB = [math]::Round($size / 1GB, 2) } } | Sort-Object SizeGB -Descending | Select-Object -First 10 }
Show-LargeFolders -Path $env:LOCALAPPDATA
Show-LargeFolders -Path $env:USERPROFILE
|
添加 -Force 参数可以确保扫描到以点开头的隐藏文件夹。
这段代码会分别列出两个位置占用空间最大的前 10 个文件夹。
常见的占用大户包括 Android\Sdk、npm-cache、Yarn\cache、Docker 等。
迁移 Android SDK
Android SDK 通常是占用空间最大的开发文件夹之一,包含平台镜像、系统镜像和构建工具。
首先停止所有 Android 相关的进程,包括 Android Studio 和模拟器。
1 2 3 4 5 6 7 8
| New-Item -ItemType Directory -Path "D:\Android\Sdk" -Force
Move-Item -Path "$env:LOCALAPPDATA\Android\Sdk" -Destination "D:\Android\Sdk" -Force
cmd /c mklink /J "$env:LOCALAPPDATA\Android\Sdk" "D:\Android\Sdk"
|
执行完成后,%LOCALAPPDATA%\Android\Sdk 路径仍然存在,但实际数据存储在 D:\Android\Sdk。
打开 Android Studio 检查 SDK Manager,确认路径识别正常。
迁移 npm 和 yarn 缓存
npm 和 yarn 的缓存目录会随着项目增多而不断增长。
1 2 3 4 5 6 7 8 9 10 11
| New-Item -ItemType Directory -Path "D:\DevRoot\npm-cache" -Force Move-Item -Path "$env:LOCALAPPDATA\npm-cache" -Destination "D:\DevRoot\npm-cache" -Force cmd /c mklink /J "$env:LOCALAPPDATA\npm-cache" "D:\DevRoot\npm-cache"
if (Test-Path "$env:LOCALAPPDATA\Yarn\cache") { New-Item -ItemType Directory -Path "D:\DevRoot\yarn-cache" -Force Move-Item -Path "$env:LOCALAPPDATA\Yarn\cache" -Destination "D:\DevRoot\yarn-cache" -Force cmd /c mklink /J "$env:LOCALAPPDATA\Yarn\cache" "D:\DevRoot\yarn-cache" }
|
npm 缓存路径也可以通过 npm config set cache 命令修改,但使用符号链接可以同时兼容 npm 和 yarn。
迁移 Gradle 缓存
Gradle 的用户主目录包含依赖缓存和 wrapper 文件,也是空间占用大户。
1 2 3 4
| New-Item -ItemType Directory -Path "D:\DevRoot\.gradle" -Force Move-Item -Path "$env:USERPROFILE\.gradle" -Destination "D:\DevRoot\.gradle" -Force cmd /c mklink /J "$env:USERPROFILE\.gradle" "D:\DevRoot\.gradle"
|
Gradle 也支持通过 GRADLE_USER_HOME 环境变量修改缓存位置,但符号链接方式无需修改任何配置。
迁移 Docker 数据
Docker Desktop 的虚拟磁盘文件通常非常大,包含所有镜像和容器数据。
Docker Desktop 提供了内置的迁移功能,推荐使用官方方式操作。
打开 Docker Desktop 设置,进入 Resources -> Advanced。
点击 Disk image location 旁边的 Browse 按钮,选择新的存储位置如 D:\DockerData。
Docker 会自动迁移所有数据,迁移完成后重启即可。
如果需要使用命令行方式,可以先导出镜像再重新导入。
1 2 3 4 5 6 7 8 9
| Stop-Service com.docker.service
Move-Item -Path "$env:LOCALAPPDATA\Docker" -Destination "D:\Docker" -Force cmd /c mklink /J "$env:LOCALAPPDATA\Docker" "D:\Docker"
Start-Service com.docker.service
|
使用官方迁移功能更为安全可靠,命令行方式仅作为备选方案。
迁移 Python 虚拟环境
如果你有大量的 Python 虚拟环境,也可以考虑迁移。
1 2 3 4
| New-Item -ItemType Directory -Path "D:\DevRoot\pip-cache" -Force Move-Item -Path "$env:LOCALAPPDATA\pip\cache" -Destination "D:\DevRoot\pip-cache" -Force cmd /c mklink /J "$env:LOCALAPPDATA\pip\cache" "D:\DevRoot\pip-cache"
|
pip 缓存也可以通过环境变量 PIP_CACHE_DIR 修改,但符号链接方式更加统一。
验证
迁移完成后,需要验证联接是否正常工作。
使用 dir 命令查看原路径,应该能看到带有 <JUNCTION> 标记的目录项。
1
| dir $env:LOCALAPPDATA\Android
|
输出中应该包含类似这样的行。
1
| 04/02/2026 10:30 AM <JUNCTION> Sdk [D:\Android\Sdk]
|
访问原路径下的文件,确认内容完整且可正常读取。
1
| Test-Path "$env:LOCALAPPDATA\Android\Sdk\platforms"
|
返回 True 表示路径可访问。
打开对应的开发工具,检查是否能正常识别和使用相关资源。
例如打开 Android Studio 检查 SDK 路径,或运行 npm install 测试缓存是否正常。
扩展
查看所有符号链接和联接点
使用以下命令可以查找系统中所有的目录联接和符号链接。
1 2
| Get-ChildItem -Path $env:LOCALAPPDATA -Recurse -Attributes ReparsePoint -ErrorAction SilentlyContinue | Select-Object FullName, Target
|
这个命令会列出所有带有重解析点的目录,方便你管理已创建的联接。
查询当前所有联接点
下面这个脚本会扫描 $env:LOCALAPPDATA 和 $env:USERPROFILE 两个目录,列出所有已创建的目录联接,并显示目标路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
$ScanPaths = @( $env:LOCALAPPDATA, $env:USERPROFILE )
function Check-Junction { param([string]$Path) try { $item = Get-Item $Path -Force -ErrorAction Stop if ($item.LinkType -eq 'Junction' -and $item.Target) { Write-Host " 联接: $Path" -ForegroundColor White Write-Host " 指向: $($item.Target)" -ForegroundColor Gray Write-Host "" return $true } } catch { } return $false }
foreach ($scanPath in $ScanPaths) { Write-Host "扫描: $scanPath (最多两层)" -ForegroundColor Cyan Write-Host "" $found = $false
$level1 = Get-ChildItem -Path $scanPath -Directory -Force -ErrorAction SilentlyContinue foreach ($d1 in $level1) { if (Check-Junction -Path $d1.FullName) { $found = $true continue }
$level2 = Get-ChildItem -Path $d1.FullName -Directory -Force -ErrorAction SilentlyContinue foreach ($d2 in $level2) { if (Check-Junction -Path $d2.FullName) { $found = $true } } }
if (-not $found) { Write-Host " 未发现联接点" -ForegroundColor Yellow } Write-Host "" }
Write-Host "=== 查询完成 ===" -ForegroundColor Cyan
|
将此脚本保存为 find-junctions.ps1 并运行即可。
同样需要注意保存为 UTF-8 with BOM 编码。
删除联接点
如果需要恢复原始状态,可以删除联接点并将数据移回。
1 2 3 4 5
| Remove-Item "$env:LOCALAPPDATA\Android\Sdk"
Move-Item -Path "D:\Android\Sdk" -Destination "$env:LOCALAPPDATA\Android\Sdk" -Force
|
注意删除联接点不会删除实际数据,数据仍然保留在目标盘中。
批量迁移脚本
如果你需要迁移多个文件夹,可以使用下面这个完整的自动化脚本。
该脚本会列出常见的前端和 Android 开发相关文件夹,逐个询问你是否迁移,确认后再执行操作。
migrate-dev-folders.ps1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
|
$TargetRoot = "D:\DevRoot"
$MigrationItems = @( @{ Source = "$env:LOCALAPPDATA\npm-cache"; Target = "npm-cache"; Desc = "npm 缓存" }, @{ Source = "$env:LOCALAPPDATA\pnpm-store"; Target = "pnpm-store"; Desc = "pnpm Store (新版)" }, @{ Source = "$env:LOCALAPPDATA\Android\Sdk"; Target = "Android\Sdk"; Desc = "Android SDK" }, @{ Source = "$env:LOCALAPPDATA\Yarn\cache"; Target = "yarn-cache"; Desc = "Yarn 缓存" }, @{ Source = "$env:LOCALAPPDATA\pnpm\cache"; Target = "pnpm-cache"; Desc = "pnpm 缓存" }, @{ Source = "$env:LOCALAPPDATA\JetBrains"; Target = "JetBrains"; Desc = "JetBrains配置" }, @{ Source = "$env:LOCALAPPDATA\pip\cache"; Target = "pip-cache"; Desc = "pip 缓存" }, @{ Source = "$env:USERPROFILE\.gradle"; Target = ".gradle"; Desc = "Gradle 缓存" }, @{ Source = "$env:USERPROFILE\.openclaw"; Target = ".openclaw"; Desc = "OpenClow" }, @{ Source = "$env:USERPROFILE\.nuget"; Target = ".nuget"; Desc = "NuGet" }, @{ Source = "$env:USERPROFILE\.vscode"; Target = ".vscode"; Desc = "VSCode配置" }, @{ Source = "$env:USERPROFILE\.m2"; Target = ".m2"; Desc = "Maven 仓库" } )
function Get-FolderSizeGB { param([string]$Path) $size = (Get-ChildItem $Path -Recurse -File -Force -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum return [math]::Round($size / 1GB, 2) }
function Create-Junction { param([string]$Source, [string]$Target) New-Item -ItemType Directory -Path (Split-Path $Target -Parent) -Force | Out-Null Move-Item -Path $Source -Destination $Target -Force cmd /c mklink /J "$Source" "$Target" | Out-Null Write-Host "成功: 已迁移 -> $Target" -ForegroundColor Green }
Write-Host "=== 开发环境文件夹迁移工具 ===" -ForegroundColor Cyan Write-Host "目标根目录: $TargetRoot" -ForegroundColor Cyan Write-Host ""
foreach ($item in $MigrationItems) { $source = $item.Source $target = Join-Path $TargetRoot $item.Target $desc = $item.Desc
if (-not (Test-Path $source)) { Write-Host "跳过: $desc ($source) 不存在" -ForegroundColor Yellow continue }
$itemAttr = (Get-Item $source).Attributes if ($itemAttr -band [System.IO.FileAttributes]::ReparsePoint) { Write-Host "跳过: $desc 已经是联接点" -ForegroundColor Yellow continue }
$sizeGB = Get-FolderSizeGB -Path $source Write-Host "发现: $desc" -ForegroundColor White Write-Host " 源路径: $source" -ForegroundColor Gray Write-Host " 目标路径: $target" -ForegroundColor Gray Write-Host " 大小: $sizeGB GB" -ForegroundColor Gray
$confirm = Read-Host "是否迁移 $desc? (y/n)" if ($confirm -eq 'y' -or $confirm -eq 'Y') { Write-Host "正在迁移 $desc ..." -ForegroundColor Cyan try { Create-Junction -Source $source -Target $target } catch { Write-Host "错误: 迁移失败: $_" -ForegroundColor Red } } else { Write-Host "跳过: $desc" -ForegroundColor Yellow } Write-Host "" }
Write-Host "=== 迁移完成 ===" -ForegroundColor Cyan
|
将此脚本保存为 migrate-dev-folders.ps1,然后以管理员身份运行 PowerShell 执行。
脚本会自动检测文件夹是否存在、是否已经是联接点,避免重复操作。
每个文件夹迁移前都会显示路径和大小,等待用户输入 y 确认后才执行。
你可以根据需要修改 $MigrationItems 数组,添加或删除要迁移的文件夹。
总结
步骤
关闭所有相关程序,确保文件夹未被占用。
在目标盘创建新的目录结构。
使用 Move-Item 将原文件夹移动到目标位置。
使用 mklink /J 在原路径创建目录联接。
验证联接是否正常工作,测试相关开发工具。
注意
迁移前务必备份重要数据,虽然操作本身不会丢失数据,但意外情况可能发生。
确保目标盘是 NTFS 或 ReFS 格式,这些文件系统才支持目录联接功能。
不要删除带有 <JUNCTION> 标记的文件夹中的实际内容,删除联接点本身不会影响目标数据。
某些程序可能会缓存原始路径信息,迁移后首次运行时可能需要重新启动程序。
如果目标盘是外部硬盘或网络驱动器,可能会影响开发工具的加载速度。
方法
常见可迁移的开发环境文件夹
Android SDK:%LOCALAPPDATA%\Android\Sdk
npm 缓存:%LOCALAPPDATA%\npm-cache
yarn 缓存:%LOCALAPPDATA%\Yarn\cache
Gradle 用户目录:%USERPROFILE%\.gradle
Docker 数据:%LOCALAPPDATA%\Docker
pip 缓存:%LOCALAPPDATA%\pip\cache
Maven 仓库:%USERPROFILE%\.m2\repository
CocoaPods 缓存(macOS):~/.cocoapods
Homebrew 缓存(macOS):~/Library/Caches/Homebrew
迁移方法统一为三步:移动数据、创建联接、验证功能。