├── README.md ├── phone_backup.ps1 └── phone_backup_recursive.ps1 /README.md: -------------------------------------------------------------------------------- 1 | # powershell-mtp-file-transfer 2 | Powershell script to copy files/folders from the Android phone connected to Windows PC. 3 | Uses `Shell.Application` object to interact with Windows explorer API to copy from device connected via MTP Protocol 4 | 5 | Sometimes the `Shell.Application` doesn't detect all files unless the folder on phone to be backed up was opened in Windows Explorer previously 6 | -------------------------------------------------------------------------------- /phone_backup.ps1: -------------------------------------------------------------------------------- 1 | #for an enhanced version that supports nested folders go to https://github.com/nosalan/powershell-mtp-file-transfer/blob/master/phone_backup_recursive.ps1 2 | 3 | $ErrorActionPreference = "Stop" 4 | $DestDirForPhotos = "C:\BACKUP\TELEFON_DCIM_ALL" 5 | $DestDirForVoiceRecordings = "C:\BACKUP\TELEFON_VOICE_RECORDINGS_ALL" 6 | 7 | 8 | function Create-Dir($path) 9 | { 10 | if(! (Test-Path -Path $path)) 11 | { 12 | Write-Host "Creating: $path" 13 | New-Item -Path $path -ItemType Directory 14 | } 15 | else 16 | { 17 | Write-Host "Path $path already exist" 18 | } 19 | } 20 | 21 | 22 | function Get-SubFolder($parentDir, $subPath) 23 | { 24 | $result = $parentDir 25 | foreach($pathSegment in ($subPath -split "\\")) 26 | { 27 | $result = $result.GetFolder.Items() | Where-Object {$_.Name -eq $pathSegment} | select -First 1 28 | if($result -eq $null) 29 | { 30 | throw "Not found $subPath folder" 31 | } 32 | } 33 | return $result; 34 | } 35 | 36 | 37 | function Get-PhoneMainDir($phoneName) 38 | { 39 | $o = New-Object -com Shell.Application 40 | $rootComputerDirectory = $o.NameSpace(0x11) 41 | $phoneDirectory = $rootComputerDirectory.Items() | Where-Object {$_.Name -eq $phoneName} | select -First 1 42 | 43 | if($phoneDirectory -eq $null) 44 | { 45 | throw "Not found '$phoneName' folder in This computer. Connect your phone." 46 | } 47 | 48 | return $phoneDirectory; 49 | } 50 | 51 | 52 | function Get-FullPathOfMtpDir($mtpDir) 53 | { 54 | $fullDirPath = "" 55 | $directory = $mtpDir.GetFolder 56 | while($directory -ne $null) 57 | { 58 | $fullDirPath = -join($directory.Title, '\', $fullDirPath) 59 | $directory = $directory.ParentFolder; 60 | } 61 | return $fullDirPath 62 | } 63 | 64 | 65 | 66 | function Copy-FromPhone-ToDestDir($sourceMtpDir, $destDirPath) 67 | { 68 | Create-Dir $destDirPath 69 | $destDirShell = (new-object -com Shell.Application).NameSpace($destDirPath) 70 | $fullSourceDirPath = Get-FullPathOfMtpDir $sourceMtpDir 71 | 72 | Write-Host "Copying from: '" $fullSourceDirPath "' to '" $destDirPath "'" 73 | 74 | $copiedCount = 0; 75 | 76 | foreach ($item in $sourceMtpDir.GetFolder.Items()) 77 | { 78 | $itemName = ($item.Name) 79 | $fullFilePath = Join-Path -Path $destDirPath -ChildPath $itemName 80 | 81 | if(Test-Path $fullFilePath) 82 | { 83 | Write-Host "Element '$itemName' already exists" 84 | } 85 | else 86 | { 87 | $copiedCount++; 88 | Write-Host ("Copying #{0}: {1}{2}" -f $copiedCount, $fullSourceDirPath, $item.Name) 89 | $destDirShell.CopyHere($item) 90 | } 91 | } 92 | Write-Host "Copied '$copiedCount' elements from '$fullSourceDirPath'" 93 | } 94 | 95 | 96 | $phoneName = "MyPhoneName" #Phone name as it appears in This PC 97 | $phoneRootDir = Get-PhoneMainDir $phoneName 98 | 99 | $phoneCardPhotosSourceDir = Get-SubFolder $phoneRootDir "Card\DCIM\Camera" 100 | Copy-FromPhone-ToDestDir $phoneCardPhotosSourceDir $DestDirForPhotos 101 | 102 | $phonePhotosSourceDir = Get-SubFolder $phoneRootDir "Phone\DCIM\Camera" 103 | Copy-FromPhone-ToDestDir $phonePhotosSourceDir $DestDirForPhotos 104 | 105 | $phoneVoiceRecorderSourceDir = Get-SubFolder $phoneRootDir "Phone\VoiceRecorder" 106 | Copy-FromPhone-ToDestDir $phoneVoiceRecorderSourceDir $DestDirForVoiceRecordings 107 | -------------------------------------------------------------------------------- /phone_backup_recursive.ps1: -------------------------------------------------------------------------------- 1 | #this is an enhanced version of https://github.com/nosalan/powershell-mtp-file-transfer/blob/master/phone_backup.ps1 2 | #it supports backing up nested folders 3 | 4 | $ErrorActionPreference = [string]"Stop" 5 | $DestDirForPhotos = [string]"C:\BACKUP\TELEFON_DCIM_ALL" 6 | $DestDirForCallRecordings = [string]"C:\BACKUP\TELEFON_CALL_RECORDINGS_ALL" 7 | $DestDirForVoiceRecordings = [string]"C:\BACKUP\TELEFON_VOICE_RECORDINGS_ALL" 8 | $DestDirForWhatsApp = [string]"C:\BACKUP\TELEFON_WHATSAPP_ALL" 9 | $DestDirForViber = [string]"C:\BACKUP\TELEFON_VIBER_ALL" 10 | $Summary = [Hashtable]@{NewFilesCount=0; ExistingFilesCount=0} 11 | 12 | function Create-Dir($path) 13 | { 14 | if(! (Test-Path -Path $path)) 15 | { 16 | Write-Host "Creating: $path" 17 | New-Item -Path $path -ItemType Directory 18 | } 19 | else 20 | { 21 | Write-Host "Path $path already exist" 22 | } 23 | } 24 | 25 | 26 | function Get-SubFolder($parentDir, $subPath) 27 | { 28 | $result = $parentDir 29 | foreach($pathSegment in ($subPath -split "\\")) 30 | { 31 | $result = $result.GetFolder.Items() | Where-Object {$_.Name -eq $pathSegment} | select -First 1 32 | if($result -eq $null) 33 | { 34 | throw "Not found $subPath folder" 35 | } 36 | } 37 | return $result; 38 | } 39 | 40 | 41 | function Get-PhoneMainDir($phoneName) 42 | { 43 | $o = New-Object -com Shell.Application 44 | $rootComputerDirectory = $o.NameSpace(0x11) 45 | $phoneDirectory = $rootComputerDirectory.Items() | Where-Object {$_.Name -eq $phoneName} | select -First 1 46 | 47 | if($phoneDirectory -eq $null) 48 | { 49 | throw "Not found '$phoneName' folder in This computer. Connect your phone." 50 | } 51 | 52 | return $phoneDirectory; 53 | } 54 | 55 | 56 | function Get-FullPathOfMtpDir($mtpDir) 57 | { 58 | $fullDirPath = "" 59 | $directory = $mtpDir.GetFolder 60 | while($directory -ne $null) 61 | { 62 | $fullDirPath = -join($directory.Title, '\', $fullDirPath) 63 | $directory = $directory.ParentFolder; 64 | } 65 | return $fullDirPath 66 | } 67 | 68 | 69 | 70 | function Copy-FromPhoneSource-ToBackup($sourceMtpDir, $destDirPath) 71 | { 72 | Create-Dir $destDirPath 73 | $destDirShell = (new-object -com Shell.Application).NameSpace($destDirPath) 74 | $fullSourceDirPath = Get-FullPathOfMtpDir $sourceMtpDir 75 | 76 | 77 | Write-Host "Copying from: '" $fullSourceDirPath "' to '" $destDirPath "'" 78 | 79 | $copiedCount, $existingCount = 0 80 | 81 | foreach ($item in $sourceMtpDir.GetFolder.Items()) 82 | { 83 | $itemName = ($item.Name) 84 | $fullFilePath = Join-Path -Path $destDirPath -ChildPath $itemName 85 | 86 | if($item.IsFolder) 87 | { 88 | Write-Host $item.Name " is folder, stepping into" 89 | Copy-FromPhoneSource-ToBackup $item (Join-Path $destDirPath $item.GetFolder.Title) 90 | } 91 | elseif(Test-Path $fullFilePath) 92 | { 93 | Write-Host "Element '$itemName' already exists" 94 | $existingCount++; 95 | } 96 | else 97 | { 98 | $copiedCount++; 99 | Write-Host ("Copying #{0}: {1}{2}" -f $copiedCount, $fullSourceDirPath, $item.Name) 100 | $destDirShell.CopyHere($item) 101 | } 102 | } 103 | $script:Summary.NewFilesCount += $copiedCount 104 | $script:Summary.ExistingFilesCount += $existingCount 105 | Write-Host "Copied '$copiedCount' elements from '$fullSourceDirPath'" 106 | } 107 | 108 | 109 | 110 | $phoneName = "MyPhoneName" #Phone name as it appears in This PC 111 | $phoneRootDir = Get-PhoneMainDir $phoneName 112 | 113 | Copy-FromPhoneSource-ToBackup (Get-SubFolder $phoneRootDir "Phone\ACRCalls") $DestDirForCallRecordings 114 | Copy-FromPhoneSource-ToBackup (Get-SubFolder $phoneRootDir "Phone\VoiceRecorder") $DestDirForVoiceRecordings 115 | Copy-FromPhoneSource-ToBackup (Get-SubFolder $phoneRootDir "Phone\WhatsApp") $DestDirForWhatsApp 116 | Copy-FromPhoneSource-ToBackup (Get-SubFolder $phoneRootDir "Phone\DCIM\Camera") $DestDirForPhotos 117 | Copy-FromPhoneSource-ToBackup (Get-SubFolder $phoneRootDir "Phone\viber") $DestDirForViber 118 | Copy-FromPhoneSource-ToBackup (Get-SubFolder $phoneRootDir "Card\DCIM\Camera") $DestDirForPhotos 119 | 120 | write-host ($Summary | out-string) --------------------------------------------------------------------------------