Download Version 1.0 copy of specific files (ideally user selects with out-gridview or equivalent)

70 Views Asked by At

Long story, but OneDrive seems to have bricked a bunch of my .SQL and .drawio files.

  • Version 2.0 (i.e. broken) -- is encoded as ANSI instead of UTF-8, files just have "NUL" inside when i open them with Notepad++

  • Version 1.0 (i.e. working) -- is encoded as UTF-8

Both versions seem to have the exact same date/time modified (at least according to the website).

Version 1.0 file works just fine when I download it, and have confirmed 2.0 is broken. So whilst I am waiting for Microsoft to help troubleshoot what happened, I do not want to have to go manually downloading each version 1.0 file clicking until my finger falls off :)

https://knowledge-junction.in/2023/04/14/small-tricks-and-tips-microsoft-365-pnp-powershell-getting-downloading-version-of-document-resolving-error-related-to-version-history-sorry-something-went-wrong-looks-like-we-cant-show-th/

That URL's PowerShell seems good (I'll quote it below just in case the link goes dead), but it's very specific about downloading a single particular file. I don't know how to modify it, so I can actively choose which files should be downloaded. I'd prefer the user be able to choose the files they want to download in some sort of GUI, but if that's not possible, maybe just restricting it to particular file extensions i.e. .drawio and .sql.

Any assistance most welcome!

try {
        #connect to SharePoint online
        Connect-PnPOnline -Url https://knowledgejunction1.sharepoint.com/sites/Demo -Interactive

        #get respective file of which we need to download the version 
        $file = Get-PnPFile -Url "/sites/Demo/Shared Documents/coverpages.png"
        $file

        $fileVersions = Get-PnPProperty -ClientObject $file -Property Versions
        $fileVersions

        $version_1 = $fileVersions.GetById(512)
        $version_1

        #set the path where we need to download and give the specific name
        $versionDocName = "C:\Prasham\Articles\PowerShell\SPO\DocumentVersionHistory\1.0_$($file.Name)"

        #here we need context 
        $pnpContext = Get-PnPContext

        #fetch contents of the file version
        $docVersionStream = $version_1.OpenBinaryStream()
        $pnpContext.ExecuteQuery()

        #download respective version of document
        [System.IO.FileStream] $objFileStream = [System.IO.File]::Open($versionDocName,[System.IO.FileMode]::OpenOrCreate)
        $docVersionStream.Value.CopyTo($objFileStream)
        $objFileStream.Close()
          
        Write-Host -f Green "Version 1.0 Downloaded to :" $versionDocName

    }
catch
{
    write-host "Error: $($_.Exception.Message)" -foregroundcolor Red
    $errorObj = New-Object PSObject -Property @{ Exceptions = "Error: $($_.Exception.Message)"}
    
    #exporting errors to CSV file
    $errorObj | Export-Csv -Append -Path   
                "C:\Prasham\Articles\PowerShell\SPO\DocumentVersionHistory\ErrorLogs.csv"
}

I'm not that great at PowerShell. I've done some googling, but I couldn't find anyone doing exactly what I needed unfortunately :(

..

SO... i managed to steal/steal and modify/write this below, it works, but is PAINFULLY slow at creating the folder structure... and actually creates way more folders than it needs to... if possible i'd like to find a way to create only the folders that i need to (based on the files that are going to be downloaded's path...) can anyone help with that please?

# define working directory of the script based on powershell version that is hosting the script
if($PSVersionTable.PSVersion.Major -lt 3){$WorkingDirectory = split-path -parent $MyInvocation.MyCommand.Definition}
else{$WorkingDirectory = $PSScriptRoot}

Import-Module PnP.PowerShell

$username = ($env:username).ToLower()
$domain = (($env:userdnsdomain).Tolower()).replace(".","_")
$upnwithunderscores = "$($username)_$domain"
$YourCompanyNameHere = 'Company'

$WebURL = "https://$($YourCompanyNameHere)-my.sharepoint.com/personal/$upnwithunderscores"

Write-host "Working with URL: " -fore white -nonewline
Write-host "$WebURL" -fore green

$listUrl        = 'Documents'
$destination    = "$WorkingDirectory\Output"
 
Try {
    #Connect to OneDrive site
    Connect-PnPOnline $WebURL -Interactive
    # -interactive Connects to the Azure AD, acquires an access token and allows PnP PowerShell to access both SharePoint and the Microsoft Graph.
    # By default it will use the PnP Management Shell multi-tenant application behind the scenes,
    # so make sure to run `Register-PnPManagementShellAccess` first.
    
    $Web = Get-PnPWeb
 
    #Get the "Documents" library where all OneDrive files are stored
    $List = Get-PnPList -Identity "Documents"
  
    #Get all Items from the Library - with progress bar
    $global:counter = 0
    $ListItems = Get-PnPListItem -List $List -PageSize 500 -Fields ID -ScriptBlock {
        Param($items)
        $global:counter += $items.Count
        Write-Progress -PercentComplete ($global:Counter / ($List.ItemCount) * 100) -Activity "Getting Items from OneDrive:" -Status "Processing Items $global:Counter to $($List.ItemCount)"
    } 
    Write-Progress -Activity "Completed Retrieving Files and Folders from OneDrive!" -Completed
    
    #
    # this is the problemetic code... it needs to be moved so instead of creating ALL the subfolders.. even if we don't care about the files inside,
    # it should only create the folders that need to exist for the files you want to download so really it needs to be creating folders inside the $FilesColl loop below...
    #
    
                                    # Get all Subfolders of the library (PAINFULLY SLOW)
                                    $SubFolders = $ListItems | Where {$_.FileSystemObjectType -eq "Folder" -and $_.FieldValues.FileLeafRef -ne "Forms"}
                                    $SubFolders | ForEach-Object {
                                        #Ensure All Folders in the Local Path
                                        $LocalFolder = $destination + ($_.FieldValues.FileRef.Substring($Web.ServerRelativeUrl.Length)) -replace "/","\"
                                        #Create Local Folder, if it doesn't exist
                                        If (!(Test-Path -Path $LocalFolder)) {
                                            New-Item -ItemType Directory -Path $LocalFolder | Out-Null
                                        }
                                        Write-host -f Yellow "Ensured Folder '$LocalFolder'"
                                    }
  
    #Get all Files from the folder
    $FilesColl =  $ListItems | Where {$_.FileSystemObjectType -eq "File"}

    #Iterate through each file and download                     -- here we define the file types i want to retrieve there is probably a better way of doing this but whatever...
    foreach ($f in ($FilesColl | ? {$_.FieldValues.FileRef -like '*.sql' -or $_.FieldValues.FileRef -like '*.drawio'})) {
        Write-host "$($f.FieldValues.FileleafRef)" -fore cyan
        $FileDownloadPath = ($destination + ($f.FieldValues.FileRef.Substring($Web.ServerRelativeUrl.Length)) -replace "/","\").Replace($f.FieldValues.FileLeafRef,'')
        
        $PNPFile = Get-PnPFile -ServerRelativeUrl $f.FieldValues.FileRef
        
        if ($PNPFIle) {
            $FileVersions = $null
            $fileVersions = Get-PnPProperty -ClientObject $PNPFile -Property Versions
            
            if ($FileVersions -ne $null) {
                #$version_1 = $fileVersions.GetById(512) # for some reason this didn't work???
                $version_1 = $null
                $version_1 = $fileVersions | ? {$_.ID -eq '512'}
                if ($Version_1 -ne $null) {
                    
                    #set the path where we need to download and give the specific name
                    $versionDocName = "$($FileDownloadPath)$($PNPFile.Name)"

                    #here we need context 
                    $pnpContext = Get-PnPContext #  retrieve the current context for the SharePoint Online Management Shell.
                    # The context is a set of variables that define the current state of the shell, such as the URL of the SharePoint Online site, the credentials of the user, and the authentication mode.

                    #fetch contents of the file version
                    $docVersionStream = $version_1.OpenBinaryStream()
                    $pnpContext.ExecuteQuery()

                    #download respective version of document
                    [System.IO.FileStream] $objFileStream = [System.IO.File]::Open($versionDocName,[System.IO.FileMode]::OpenOrCreate)
                    $docVersionStream.Value.CopyTo($objFileStream)
                    $objFileStream.Close()
                    
                    Write-Host "Version 1.0 Downloaded to : " -f white -nonewline
                    Write-Host "$versionDocName" -fore green

                } else {
                    Write-host "Unable to find Version 1 of: $($PNPFIle.Name)"
                }
            }
        }
        Write-host -f Green "Downloaded File from '$($f.FieldValues.FileRef)'"
        
    }
} Catch {
    write-host "Error: $($_.Exception.Message)" -foregroundcolor Red
}
0

There are 0 best solutions below