开发环境大瘦身:将占用空间的文件夹迁移到其他盘

前言

开发人员的 C 盘经常会被各种 SDK、缓存和依赖包塞满。Android SDK、npm 全局包、Docker 镜像、Gradle 缓存等文件夹动辄占用几十 GB 空间。直接删除会导致开发工具无法正常工作,手动修改每个工具的配置路径又繁琐且容易遗漏。

使用 Windows 的目录联接(Directory Junction)功能,可以在不改变原路径的情况下,将文件夹的实际数据存储在其他盘。

这种方法对开发工具完全透明,工具仍然访问原来的路径,但数据实际存储在目标盘。

本文将介绍如何安全地迁移常见的开发环境文件夹,包括 Android SDK、npm 缓存、Gradle 缓存等。

原理说明

Windows 提供了 mklink /J 命令来创建目录联接。

目录联接类似于符号链接,它会在原路径创建一个”快捷方式”,指向新的实际存储位置。

当程序访问原路径时,系统会自动重定向到目标路径,程序本身无需任何修改。

这种方式的优势在于对应用程序完全透明,不需要修改任何环境变量或配置文件。

迁移前的准备工作

在开始迁移之前,需要完成以下准备工作。

  1. 关闭所有可能使用目标文件夹的程序,包括 IDE、终端、后台服务等。

  2. 确认目标盘有足够的可用空间,建议预留比原文件夹大 10% 的空间。

  3. 以管理员身份打开命令提示符或 PowerShell,确保有足够的权限执行操作。

  4. 记录原文件夹的完整路径,后续需要创建相同名称的联接点。

实现

查找占用空间的文件夹

首先需要找出哪些开发相关的文件夹占用了大量空间。

使用以下 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
}

# 扫描 AppData\Local
Show-LargeFolders -Path $env:LOCALAPPDATA

# 扫描用户目录(包含 .gradle、.m2 等隐藏文件夹)
Show-LargeFolders -Path $env:USERPROFILE

添加 -Force 参数可以确保扫描到以点开头的隐藏文件夹。

这段代码会分别列出两个位置占用空间最大的前 10 个文件夹。

常见的占用大户包括 Android\Sdknpm-cacheYarn\cacheDocker 等。

迁移 Android SDK

Android SDK 通常是占用空间最大的开发文件夹之一,包含平台镜像、系统镜像和构建工具。

首先停止所有 Android 相关的进程,包括 Android Studio 和模拟器。

1
2
3
4
5
6
7
8
# 1. 创建目标目录
New-Item -ItemType Directory -Path "D:\Android\Sdk" -Force

# 2. 移动原文件夹到新位置
Move-Item -Path "$env:LOCALAPPDATA\Android\Sdk" -Destination "D:\Android\Sdk" -Force

# 3. 创建目录联接
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
# 迁移 npm 缓存
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"

# 迁移 yarn 缓存
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
# 迁移 Gradle 用户目录
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
# 停止 Docker
Stop-Service com.docker.service

# 移动数据目录(示例路径,实际路径可能不同)
Move-Item -Path "$env:LOCALAPPDATA\Docker" -Destination "D:\Docker" -Force
cmd /c mklink /J "$env:LOCALAPPDATA\Docker" "D:\Docker"

# 重启 Docker
Start-Service com.docker.service

使用官方迁移功能更为安全可靠,命令行方式仅作为备选方案。

迁移 Python 虚拟环境

如果你有大量的 Python 虚拟环境,也可以考虑迁移。

1
2
3
4
# 迁移 pip 缓存
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
# find-junctions.ps1
# 查询指定目录下的所有目录联接(Junction)
# 最多扫描两层子目录,兼容 PowerShell 5.1

$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
# migrate-dev-folders.ps1
# 迁移开发环境文件夹到指定盘
# 需要以管理员身份运行 PowerShell
# 重要:保存时请使用 UTF-8 with BOM 编码,否则 PowerShell 5.1 会因中文乱码报错

$TargetRoot = "D:\DevRoot"

# 定义常见前端/Android 开发相关文件夹
$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 数组,添加或删除要迁移的文件夹。

总结

步骤

  1. 关闭所有相关程序,确保文件夹未被占用。

  2. 在目标盘创建新的目录结构。

  3. 使用 Move-Item 将原文件夹移动到目标位置。

  4. 使用 mklink /J 在原路径创建目录联接。

  5. 验证联接是否正常工作,测试相关开发工具。

注意

迁移前务必备份重要数据,虽然操作本身不会丢失数据,但意外情况可能发生。

确保目标盘是 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

迁移方法统一为三步:移动数据、创建联接、验证功能。