2025-07-30

Export SharePoint Files with Path hierarchy

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

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

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

ExportSharePointSiteTriggler.ps1

#$Finalize = $True;

#Add-PSSnapin Microsoft.SharePoint.PowerShell
#(Get-SPSite -Limit ALL)

$TargetSites = @();
$TargetSites += 'Site1';
$TargetSites += 'Site2';
$TargetSites += 'Site3';

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

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


ExportSharePointSiteMain.ps1

param (

[String]$TargetSite = ''
);

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

$TargetPath = 'S:\SharePointData';

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

$FinalResult = '';
foreach ($Site in $Sites) {

if ($Finalize -ne $Null) {
if ($Finalize -eq $True) {
$Site.ReadOnly = $True;
$Site.LockState = 'ReadOnly';
# NoAccess
# ReadOnly
# Unlock
};
};

$FailedEventCount = 0;

$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')
$TranscriptLog = $LogPath + ("Export SharePoint Site - " + ($Site.RootWeb.Title) + ' - ' + $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");
$Files = @();
$FilesIndex = 0;
$TotalFilesCapacity = 0;
$SiteObject = Get-SPSite $Site.Url;
$Lists = (($SiteObject.RootWeb).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 -eq 'Style Library\Media Player') {
continue;
};

$FileURL = ($SiteObject.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);
$TotalFilesCapacity += $File.Length;
$Files += New-Object -TypeName psobject -Property @{
Url = $FileURL;
Name = $FileName;
Length = $File.Length;
Site = [String]$SiteObject.RootWeb.Title;
Folder = $ParentFolder;
PathLength = (([String]$SiteObject.RootWeb.Title + '\' + $ParentFolder + '\' + $FileName).Length + 3)
FolderLength = (([String]$SiteObject.RootWeb.Title + '\' + $ParentFolder).Length + 3)
};
};
};

$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 ' + [String]$SiteObject.RootWeb.Title + ' are too long')
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Some Files Path of ' + [String]$SiteObject.RootWeb.Title + ' are too long' + "`r`n");
$PathLengthOver
};

$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 ' + [String]$SiteObject.RootWeb.Title + ' are too long');
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Some Folder Path of ' + [String]$SiteObject.RootWeb.Title + ' are too long' + "`r`n");
$PathLengthOver
};

$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 "' + [String]$SiteObject.RootWeb.Title + '": ' + (HumanReadableByteSize($TotalFilesCapacity)));
write-host ('--------------------------------------------');
write-host ('')
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Total Files Capacity of "' + [String]$SiteObject.RootWeb.Title + '": ' + (HumanReadableByteSize($TotalFilesCapacity)) + "`r`n");

$Index = 0;
$SiteRootPath = ($TargetPath + '\' + $Files[$Index].Site);
$SiteJunctionPath = ('S:\' + $Files[$Index].Site);
if ((Test-Path -LiteralPath $SiteJunctionPath) -eq $True) {
$command = ("junction -d `"" + $SiteJunctionPath + "`"");
$DelSiteJunctionPathResult = Invoke-Expression $command;
if ((Test-Path -LiteralPath $SiteJunctionPath) -eq $True) {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Clean Junction for Site : ' + $Files[$Index].Site);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Clean Junction for Site : ' + $Files[$Index].Site + "`r`n");
Continue;
};
};
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
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 for Site : ' + $Files[$Index].Site);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Junction 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 for Site : ' + $Files[$Index].Site);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Failed to Create Junction 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 ('')

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`tLength`t" + $FileLengthShow + ($OutPath + '\' + $File.Name));
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Existed`t`tLength`t" + $FileLengthShow + ($OutPath + '\' + $File.Name) + "`r`n");
} else {
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Download`tLength`t" + $FileLengthShow + $File.Url + ' to ' + ($OutPath + '\' + $File.Name));
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Download`tLength`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;
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`tLength`t" + $FileLengthShow + $File.Url + ' to ' + ($OutPath + '\' + $File.Name));
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' ' + $FilesIndexShow + "Failed`t`tLength`t" + $FileLengthShow + $File.Url + ' to ' + ($OutPath + '\' + $File.Name) + "`r`n");
$FailedEventCount++;
};
};
};
};

write-host ('');
$FinalResult += ("`r`n");

Set-Location -LiteralPath $Location;

$AllDownloadedFiles = (Get-ChildItem -File -Path $SiteJunctionPath -Recurse);

write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Site 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 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 "' + [String]$SiteObject.RootWeb.Title + '" Download Complete.');
write-host ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' FailedEventCount : "' + $FailedEventCount);
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' Site "' + [String]$SiteObject.RootWeb.Title + '" Download Complete.' + "`r`n");
$FinalResult += ((Get-Date).ToUniversalTime().AddHours(8).ToString('yyyy-MM-dd HH:mm:ss') + ' FailedEventCount : "' + $FailedEventCount + "`r`n");

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

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

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


沒有留言:

張貼留言