2025-07-30

Export SharePoint Files with Path hierarchy

利用 Invoke-RestMethod 透過網址下載
需要具有 Site 管理權限

搭配 Junction 避免路徑過長的問題
如果還是過長,要再另外處理

注意非黑色文字的變數與執行細節

ExportSharePointSiteTriggler.ps1

$Finalize = $False;

$ConcurrentLimit = 5; 

Add-PSSnapin Microsoft.SharePoint.PowerShell
$Sites = (Get-SPSite -Limit ALL) 

$ExceptSites = @();
$ExceptSites += 'DefaultSite';

$TargetSites = @();
foreach ($SiteURL in ($Sites | where {(($_.Url -Split '/').count -eq 5) -and ($ExceptSites -notcontains ($_.Url -Split '/')[-1])})) {
$TargetSites += ($SiteURL.Url -Split '/')[-1];
};

$Location = 'S:\Command';
Set-Location -LiteralPath $Location;

foreach ($TargetSite in $TargetSites) {
write-host ('Processing ' + $TargetSite);
Start-Process "C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" -ArgumentList "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", ($Location + '\ExportSharePointSiteMain.ps1'), "-TargetSite", $TargetSite

while ((Get-Process -Name 'PowerShell').Count -ge $ConcurrentLimit) {
Start-Sleep -Seconds 1;
};
};


ExportSharePointSiteMain.ps1

param (
[String]$TargetSite = ''
);

if ($TargetSite -eq '') {
exit;
};

$TargetPath = 'S:\SharePointData';

Add-PSSnapin Microsoft.SharePoint.PowerShell
$Site = (Get-SPSite -Limit ALL) | where {(($_.Url -Split '/')[-1] -eq $TargetSite)}
$FinalResult = '';

if ($Finalize -ne $Null) {
if ($Finalize -eq $True) {
write-host ('Set Site: ' + $Site.Url + ' to Read Only');
$Site.ReadOnly = $True;
};
};
$Location = 'S:\Command';
Set-Location -LiteralPath $Location;
$LogPath = ($Location + "\Logs\");
if ((Test-Path -LiteralPath $LogPath) -ne $True) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + " Create Folder: " + $LogPath);
write-host
New-Item -ItemType Directory -Path $LogPath -Force | Out-Null;
};
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'
$Global:Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False;

if ($Credential -eq $Null) {
$CredentailFileName = "Credential.txt";
[string[]]$CredentialRead = Get-Content -Path $CredentailFileName
$UserName = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($CredentialRead[0]))
$Password = $CredentialRead[1] | ConvertTo-SecureString -Key (1..16)
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $UserName, $Password
};
if ((Get-Command HumanReadableByteSize -erroraction silentlycontinue) -eq $Null) {
function HumanReadableByteSize($size) {
switch ($size) {
{$_ -gt 1TB} {($size / 1TB).ToString("n2") + " TB";break};
{$_ -gt 1GB} {($size / 1GB).ToString("n2") + " GB";break};
{$_ -gt 1MB} {($size / 1MB).ToString("n2") + " MB";break};
{$_ -gt 1KB} {($size / 1KB).ToString("n2") + " KB";break};
default {"$size B"};
};
};
};

$ScriptStartTime = (Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss');
$DateTimeString = (Get-Date).ToUniversalTime().AddHours(8).ToString('yyyyMMdd HHmmss')
$LogHead = 'Export';
if ($Finalize -eq $True) {
$LogHead = 'Finalize';
};
$TranscriptLog = $LogPath + ($LogHead + " SharePoint Site - " + $TargetSite + ' - ' + $DateTimeString + ".log")
$LogStatus = start-transcript -LiteralPath $TranscriptLog

write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Working on ' + $Site.RootWeb.Title + ' ' + $Site);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Working on ' + $Site.RootWeb.Title + ' ' + $Site + "`r`n");
$SiteObject = Get-SPSite $Site.Url;
$WebObjects = $SiteObject | Get-SPWeb -Limit All
foreach ($WebObject in $WebObjects) {
$FailedEventCount = 0;

if ($WebObject.IsRootWeb -eq $True) {
$ParentFolderPreName = 'RootSite';
} else {
$ParentFolderPreName = 'SubSite';
};

write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Start Processing "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '"');
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Start Processing "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '"' + "`r`n");

$Files = @();
$FilesIndex = 0;
$TotalFilesCapacity = 0;
$Lists = @();
$Lists += ($WebObject.Lists | where {$_.BaseTemplate -eq 101});
foreach ($List in $Lists) {
foreach ($File in ($List.Items).File) {
$FileName = (($File.Url -Split '/')[-1]);
if ($FileName -eq 'Thumbs.db') {
Continue;
};
$FilesIndex++;
$ParentFolder = ($File.ParentFolder.Url -Split '/');
$ParentFolder[0] = $File.DocumentLibrary.Title;
$ParentFolder = ($ParentFolder -Join '\');

if (($ParentFolder -Split '\\')[0] -eq 'Style Library') {
continue;
};
if ($ParentFolder -eq 'Reporting Templates') {
continue;
};
$ExportPath = ($TargetSite + '\' + $ParentFolderPreName + '\' + $WebObject.Title);
$FileURL = ($WebObject.Url + '/' + $File.Url);
$FileLengthShow = (HumanReadableByteSize($File.Length));
if ($FileLengthShow.Length -lt 12) {
$FileLengthShow += "`t";
};
if ($FileLengthShow.Length -lt 8) {
$FileLengthShow += "`t";
};
if ($FileLengthShow.Length -lt 4) {
$FileLengthShow += "`t";
};
$FileLengthShow += "`t";
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + [String]$FilesIndex + "`t" + $FileLengthShow + $FileURL);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + [String]$FilesIndex + "`t" + $FileLengthShow + $FileURL + "`r`n");

$TotalFilesCapacity += $File.Length;
$Files += New-Object -TypeName psobject -Property @{
Url = $FileURL;
Name = $FileName;
Length = $File.Length;
Site = [String]$WebObject.Title;
ExportPath = $ExportPath;
Folder = $ParentFolder;
PathLength = (($ParentFolder + '\' + $FileName).Length + 8)
FolderLength = (($ParentFolder).Length + 8)
};
};
};
if ($Files.Count -ne 0) {
$PathLengthOver = @();
$PathLengthOver += ($Files | where {$_.PathLength -ge 260});
if ($PathLengthOver.Count -gt 0) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Some Files Path of "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '" are too long')
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Some Files Path of "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '" are too long' + "`r`n");
$PathLengthOver
$FailedEventCount++;
};
$FolderLengthOver = @();
$FolderLengthOver = ($Files | where {$_.FolderLength -ge 248});
if ($FolderLengthOver.Count -gt 0) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Some Folder Path of "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '" are too long');
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Some Folder Path of "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '" are too long' + "`r`n");
$FolderLengthOver
$FailedEventCount++;
};
$Files = $Files | Sort-Object Name | Sort-Object Folder;
write-host ('')
write-host ('--------------------------------------------');
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Total Files Capacity of "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '": ' + (HumanReadableByteSize($TotalFilesCapacity)));
write-host ('--------------------------------------------');
write-host ('')
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Total Files Capacity of "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '": ' + (HumanReadableByteSize($TotalFilesCapacity)) + "`r`n");
$Index = 0;
$SiteRootPath = ($TargetPath + '\' + $Files[$Index].ExportPath);
$RandomPathName = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".tochararray() | sort {Get-Random})[0] -join ''
while ((Test-Path -LiteralPath ('S:\' + $RandomPathName)) -eq $True) {
$RandomPathName = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".tochararray() | sort {Get-Random})[0] -join ''
};
$SiteJunctionPath = ('S:\' + $RandomPathName);
$SiteJunctionPath
if ((Test-Path -LiteralPath $SiteRootPath) -ne $True) {
New-Item -ItemType Directory -Path $SiteRootPath -Force | Out-Null;
};
if ((Test-Path -LiteralPath $SiteRootPath) -ne $True) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Root Path: ' + $SiteRootPath);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Root Path: ' + $SiteRootPath + "`r`n");
Continue;
} else {
$command = ("junction `"" + $SiteJunctionPath + "`" `"" + $SiteRootPath + "`"");
$NewSiteJunctionResult = Invoke-Expression $command
Start-Sleep 5
if (($NewSiteJunctionResult | where {$_ -like ('*Created: ' + $SiteJunctionPath + '*')}) -eq $Null) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Junction "' + $SiteJunctionPath + '" for Site : ' + $Files[$Index].Site);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Junction "' + $SiteJunctionPath + '" for Site : ' + $Files[$Index].Site + "`r`n");
Continue;
};
if ((Test-Path -LiteralPath $SiteJunctionPath) -ne $True) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Junction "' + $SiteJunctionPath + '" for Site : ' + $Files[$Index].Site);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Junction "' + $SiteJunctionPath + '" for Site : ' + $Files[$Index].Site + "`r`n");
Continue;
};
};

write-host ('')
write-host ('--------------------------------------------');
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Begin Download Files');
write-host ('--------------------------------------------');
write-host ('')
$Index = 0;
foreach ($File in $Files) {
$Index++;
$OutPath = ($SiteJunctionPath + '\' + $File.Folder);
if ((Test-Path -LiteralPath $OutPath) -ne $True) {
New-Item -ItemType Directory -Path $OutPath -Force | Out-Null;
};
if ((Test-Path -LiteralPath $OutPath) -ne $True) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Path: ' + $OutPath);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Path: ' + $OutPath + "`r`n");
$FailedEventCount++;
} else {
$FileExisted = $False;
if ((Test-Path -LiteralPath ($OutPath + '\' + $File.Name)) -eq $True) {
$ExistedFile = Get-Item -LiteralPath ($OutPath + '\' + $File.Name);
if ($ExistedFile.Length -eq $File.Length) {
$FileExisted = $True;
};
};
$FileLengthShow = (HumanReadableByteSize($File.Length));
if ($FileLengthShow.Length -lt 11) {
$FileLengthShow += "`t";
};
if ($FileLengthShow.Length -lt 7) {
$FileLengthShow += "`t";
};
if ($FileLengthShow.Length -lt 3) {
$FileLengthShow += "`t";
};
$FileLengthShow += "`t";
$FilesIndexShow = [String]$Index + "/" + $Files.Count;
if ($FilesIndexShow.Length -lt 11) {
$FilesIndexShow += "`t";
};
if ($FilesIndexShow.Length -lt 7) {
$FilesIndexShow += "`t";
};
if ($FilesIndexShow.Length -lt 3) {
$FilesIndexShow += "`t";
};
$FilesIndexShow += "`t";
if ($FileExisted -eq $True) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Existed`t`tFileSize`t" + $FileLengthShow + ($OutPath + '\' + $File.Name));
#$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Existed`t`tFileSize`t" + $FileLengthShow + ($OutPath + '\' + $File.Name) + "`r`n");
} else {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Download`tFileSize`t" + $FileLengthShow + $File.Url + ' to ' + ($OutPath + '\' + $File.Name));
#$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Download`tFileSize`t" + $FileLengthShow + $File.Url + ' to ' + ($OutPath + '\' + $File.Name) + "`r`n");
$SafeName = $File.Name;
$SafeName = (($SafeName -replace '\[','`[') -replace '\]','`]');
Set-Location -LiteralPath $OutPath
Invoke-RestMethod -Uri $File.Url -OutFile $SafeName -Method Get -Credential $Credential;
Start-Sleep -Seconds 1;
if ($SafeName -ne $File.Name) {
Rename-Item -LiteralPath ($OutPath + '\' + $SafeName) -NewName ($OutPath + '\' + $File.Name);
};
if ((Test-Path -LiteralPath ($OutPath + '\' + $File.Name)) -ne $True) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Failed`t`tFileSize`t" + $FileLengthShow + $File.Url + ' to ' + ($OutPath + '\' + $File.Name));
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Failed`t`tFileSize`t" + $FileLengthShow + $File.Url + ' to ' + ($OutPath + '\' + $File.Name) + "`r`n");
$FailedEventCount++;
} else {
$ExistedFile = Get-Item -LiteralPath ($OutPath + '\' + $File.Name);
if ($ExistedFile.Length -ne $File.Length) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Failed`t`tFileSize`t" + (HumanReadableByteSize($ExistedFile.Length)) + '/' + $FileLengthShow + $File.Url + ' to ' + ($OutPath + '\' + $File.Name));
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Failed`t`tFileSize`t" + (HumanReadableByteSize($ExistedFile.Length)) + '/' + $FileLengthShow + $File.Url + ' to ' + ($OutPath + '\' + $File.Name) + "`r`n");
$FailedEventCount++;
};
};
};
};
};
};
write-host ('');
$FinalResult += ("`r`n");
Set-Location -LiteralPath $Location;

$AllDownloadedFiles = @();
if ($SiteJunctionPath -ne $Null) {
$AllDownloadedFiles += (Get-ChildItem -File -Path $SiteJunctionPath -Recurse);
};

write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Site "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '" Files Count - ' + $Files.Count);
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Download Files Count - ' + $AllDownloadedFiles.Count);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Site "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '" Files Count - ' + $Files.Count + "`r`n");
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Download Files Count - ' + $AllDownloadedFiles.Count + "`r`n");
write-host ('');
$FinalResult += ("`r`n");
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Site "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '" Download Complete.');
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Site "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '"Failed Event Count : ' + $FailedEventCount);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Site "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '" Download Complete.' + "`r`n");
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Site "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '"Failed Event Count : ' + $FailedEventCount + "`r`n");
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' End Processing "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '"');
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' End Processing "' + $ParentFolderPreName + '" - "' + [String]$WebObject.Title + '"' + "`r`n");

$command = ("junction -d `"" + $SiteJunctionPath + "`"");
$DelSiteJunctionPathResult = Invoke-Expression $command;
Start-Sleep 5
};

write-host ("`r`n");
write-host ("`r`n");
write-host ("----- Final Report -----");
write-host ("`r`n");
write-host ("`r`n");
write-host $FinalResult;
$LogStatus = stop-transcript

write-host ('Log File: ' + $TranscriptLog);
write-host ("`r`n");

沒有留言:

張貼留言