每個愛好拍照者都有的苦惱,就是想找到過去拍過的某一張照片。硬碟裡幾千幾百張照片,就算神仙也記不得。這時,縮圖目錄就能派上用場了。

可以製作縮圖目錄 (contact sheet) 的工具很多,Photoshop 和 Canon DPP 都可以辦到。不過痛苦的地方是,我的照片都是按照拍攝日期歸檔在資料夾裡,想要製作某年份的縮圖目錄,還得一個個地把上百個資料夾下的幾千張照片都加到軟體裡。這一回,試試看用 Claude 來簡化這個步驟。

原本使用 Canon DPP 時,方法是建立新的相片集 (collection),然後依序將該年份的照片全都新增到相片集中。最後按照拍攝日期/時間排序好,再透過列印到 PDF 的方式產生該年份的縮圖目錄。最終,我可以得到一個有著許多頁照片縮圖的 PDF。這 PDF 不但我自己愛用,Esther 也喜歡,甚至放到手機裡。


問了問 Claude,由於 Canon 沒有開放 DPP 的 API,所以外部程式無法直接在 DPP 中新增 collection 並放入照片。替代的方式是,可以新增一個資料夾,然後在 Windows 裡放入所有照片資料夾中的照片捷徑,然後就從 DPP 裡開啟這個資料夾並製作縮圖目錄就好。這種方式只需要使用 Powershell,方便多了。



測試了一下,Canon DPP 並無法正確辨識 Windows 捷徑 (.lnk),但可以讀取符號連結 (symbolic link)。符號連結是在檔案系統層級的功能,連結會被視為 CR3 檔案本身。



就此,整個動作簡單了起來。Claude 產生了兩支檔案,分別是 Powershell 指令的 Make-SymlinkFolder.ps1 檔案,以及直接執行 .ps1 檔案的 Make-SymlinkFolder.bat 批次檔。我執行批次檔,讓程式在桌面新增了 DPP_Test_2025 資料夾,裡面放入硬碟中 2025 年所有資料夾下的所有照片,總數是 3400 多張。




接著啟動 Canon DPP,瀏覽到這個資料夾,全選後按照拍照日期排序,如往常般列印成 PDF 即可。我起碼可以省下上百次選取照片並加入到 collection 的動作。





Make-SymlinkFolder.ps1
<#
.SYNOPSIS
在暫存資料夾中,建立指定年份資料夾下所有 .CR3 檔案的符號連結,方便在 Canon DPP 中測試瀏覽/Collection 功能。
.DESCRIPTION
- 掃描指定的年份資料夾 (例如 D:\Family\2026) 底下所有 yyyy-MM-dd 日期子資料夾
- 找出所有 .CR3 檔案
- 在暫存資料夾中為每個檔案建立符號連結 (symbolic link),檔名會加上日期前綴
(例如 2026-08-28_IMG_0001.CR3),避免不同天但同檔名的照片互相覆蓋
- 符號連結本身幾乎不佔空間,只是指向原始檔案的連結,不會複製或搬動照片,
但在檔案系統層級會直接呈現為 .CR3 檔案,一般程式 (包含 DPP) 應該能正常讀取
.NOTES
重要:建立符號連結需要系統管理員權限!
請用「以系統管理員身分執行」開啟 PowerShell 或對應的批次檔,
否則會出現「您沒有此作業所需的權限」之類的錯誤。
使用方式:
.\Make-SymlinkFolder.ps1 -YearFolder "D:\Family\2026"
指定暫存資料夾位置 (預設在桌面上建立 DPP_Test_<年份> 資料夾):
.\Make-SymlinkFolder.ps1 -YearFolder "D:\Family\2026" -DestFolder "D:\DPP_Test"
#>
param(
[Parameter(Mandatory = $true)]
[string]$YearFolder,
[string]$DestFolder = ""
)
# 檢查是否為系統管理員權限執行
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
$isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Error "建立符號連結需要系統管理員權限,請以「系統管理員身分執行」重新開啟 PowerShell 或批次檔再試一次。"
exit 1
}
if (-not (Test-Path $YearFolder)) {
Write-Error "找不到資料夾 $YearFolder,請確認路徑是否正確。"
exit 1
}
if ([string]::IsNullOrWhiteSpace($DestFolder)) {
$yearName = Split-Path $YearFolder -Leaf
$DestFolder = Join-Path ([Environment]::GetFolderPath("Desktop")) "DPP_Test_$yearName"
}
# 清空暫存資料夾內舊的符號連結,確保每次執行結果是最新的
# (只刪除 .CR3 符號連結本身,不會動到連結指向的原始照片)
if (Test-Path $DestFolder) {
Get-ChildItem -Path $DestFolder -Filter "*.CR3" -File | ForEach-Object {
if ($_.LinkType -eq "SymbolicLink") {
Remove-Item -Path $_.FullName -Force
}
}
} else {
New-Item -ItemType Directory -Path $DestFolder -Force | Out-Null
}
Write-Host "正在掃描 $YearFolder 底下所有 .CR3 檔案..."
$cr3Files = Get-ChildItem -Path $YearFolder -Recurse -Filter "*.cr3" -File
if ($cr3Files.Count -eq 0) {
Write-Host "找不到任何 .cr3 檔案。"
exit 0
}
Write-Host "共找到 $($cr3Files.Count) 個檔案,開始建立符號連結..."
$counter = 0
$failCount = 0
foreach ($file in $cr3Files) {
$dateFolderName = $file.Directory.Name # 例如 "2026-08-28"
$linkName = "${dateFolderName}_$($file.Name)"
$linkPath = Join-Path $DestFolder $linkName
# 避免極少數同名衝突,加上流水號
$i = 1
while (Test-Path $linkPath) {
$linkName = "${dateFolderName}_$($file.BaseName)_$i$($file.Extension)"
$linkPath = Join-Path $DestFolder $linkName
$i++
}
try {
New-Item -ItemType SymbolicLink -Path $linkPath -Target $file.FullName -ErrorAction Stop | Out-Null
$counter++
}
catch {
Write-Warning "建立符號連結失敗:$($file.FullName) -> $($_.Exception.Message)"
$failCount++
}
Write-Progress -Activity "建立符號連結中" -Status "$($counter + $failCount) / $($cr3Files.Count)" -PercentComplete ((($counter + $failCount) / $cr3Files.Count) * 100)
}
Write-Host "完成!成功建立 $counter 個符號連結於:$DestFolder"
if ($failCount -gt 0) {
Write-Host "有 $failCount 個檔案建立失敗,請確認上方警告訊息。"
}
Make-SymlinkFolder.bat
@echo off
REM ============================================================
REM Create symbolic links for all CR3 files under a year folder
REM This MUST run as Administrator (symbolic links require it)
REM Edit FAMILY_ROOT and SCRIPT_PATH below if paths are different
REM ============================================================
set SCRIPT_PATH=E:\Make-SymlinkFolder.ps1
set FAMILY_ROOT=D:\Family
set /p YEAR=Please enter the year (e.g. 2025):
if "%YEAR%"=="" (
echo No year entered. Exiting.
pause >nul
exit /b 1
)
set YEAR_FOLDER=%FAMILY_ROOT%\%YEAR%
echo.
echo Target folder: %YEAR_FOLDER%
echo.
powershell -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_PATH%" -YearFolder "%YEAR_FOLDER%"
echo.
echo ============================================================
echo Done. Please check above for any error messages.
echo This window will not close automatically - press any key.
echo ============================================================
pause >nul