Public/Drive/Start-GSDriveFileUpload.ps1
function Start-GSDriveFileUpload { <# .SYNOPSIS Starts uploading a file or list of files to Drive asynchronously .DESCRIPTION Starts uploading a file or list of files to Drive asynchronously. Allows full folder structure uploads by passing a folder as -Path and including the -Recurse parameter .PARAMETER Path The path of the file or folder to upload .PARAMETER Name The new name of the file once uploaded Defaults to the existing name of the file or folder .PARAMETER Description The description of the file or folder in Drive .PARAMETER Parents The unique Id of the parent folder in Drive to upload the file to Defaults to the root folder in My Drive .PARAMETER Recurse If $true and there is a Directory passed to -Path, this will rebuild the folder structure in Drive under the Parent Id and upload the files within accordingly .PARAMETER Wait If $true, waits for all uploads to complete and shows progress around the total upload .PARAMETER RetryCount How many times uploads should be retried when using the -Wait parameter Defaults to 10 .PARAMETER ThrottleLimit The limit of files to upload per batch while waiting .PARAMETER User The email or unique Id of the user to upload the files for .EXAMPLE Start-GSDriveFileUpload -Path "C:\Scripts","C:\Modules" -Recurse -Wait Starts uploading the Scripts and Modules folders and the files within them and waits for the uploads to complete, showing progress as files are uploaded #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('FullName')] [ValidateScript( {Test-Path $_})] [String[]] $Path, [parameter(Mandatory = $false)] [String] $Name, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false)] [String[]] $Parents, [parameter(Mandatory = $false)] [Switch] $Recurse, [parameter(Mandatory = $false)] [Switch] $Wait, [parameter(Mandatory = $false)] [Int] $RetryCount = 10, [parameter(Mandatory = $false)] [ValidateRange(1,1000)] [Int] $ThrottleLimit = 20, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams $taskList = [System.Collections.ArrayList]@() $fullTaskList = [System.Collections.ArrayList]@() $start = Get-Date $folIdHash = @{} $throttleCount = 0 $totalThrottleCount = 0 } Process { try { foreach ($file in $Path) { $details = Get-Item $file if ($details.PSIsContainer) { $newFolPerms = @{ Name = $details.Name Type = 'DriveFolder' Verbose = $false } if ($PSBoundParameters.Keys -contains 'Parents') { $newFolPerms['Parents'] = $PSBoundParameters['Parents'] } Write-Verbose "Creating new Drive folder '$($details.Name)'" $id = New-GSDriveFile @newFolPerms | Select-Object -ExpandProperty Id $folIdHash[$details.FullName] = $id if ($Recurse) { $recurseList = Get-ChildItem $details.FullName -Recurse $recDirs = $recurseList | Where-Object {$_.PSIsContainer} | Sort-Object FullName if ($recDirs) { Write-Verbose "Creating recursive folder structure under '$($details.Name)'" $recDirs | ForEach-Object { $parPath = "$(Split-Path $_.FullName -Parent)" $newFolPerms = @{ Name = $_.Name Type = 'DriveFolder' Parents = [String[]]$folIdHash[$parPath] Verbose = $false } $id = New-GSDriveFile @newFolPerms | Select-Object -ExpandProperty Id $folIdHash[$_.FullName] = $id } } $details = $recurseList | Where-Object {!$_.PSIsContainer} | Sort-Object FullName $checkFolIdHash = $true $totalFiles = $details.Count } } else { $checkFolIdHash = $false } foreach ($detPart in $details) { $throttleCount++ $contentType = Get-MimeType $detPart $body = New-Object 'Google.Apis.Drive.v3.Data.File' -Property @{ Name = [String]$detPart.Name } if (!$checkFolIdHash -and ($PSBoundParameters.Keys -contains 'Parents')) { if ($Parents) { $body.Parents = [String[]]$Parents } } elseif ($checkFolIdHash) { $parPath = "$(Split-Path $detPart.FullName -Parent)" $body.Parents = [String[]]$folIdHash[$parPath] } if ($Description) { $body.Description = $Description } $stream = New-Object 'System.IO.FileStream' $detPart.FullName,'Open','Read' $request = $service.Files.Create($body,$stream,$contentType) $request.QuotaUser = $User $request.SupportsTeamDrives = $true $request.ChunkSize = 512KB $upload = $request.UploadAsync() $task = $upload.ContinueWith([System.Action[System.Threading.Tasks.Task]] {$stream.Dispose()}) Write-Verbose "[$($detPart.Name)] Upload Id $($upload.Id) has started" if (!$Script:DriveUploadTasks) { $Script:DriveUploadTasks = [System.Collections.ArrayList]@() } $script:DriveUploadTasks += [PSCustomObject]@{ Id = $upload.Id File = $detPart Length = $detPart.Length SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) StartTime = $(Get-Date) Parents = $body.Parents User = $User Upload = $upload Request = $request } $taskList += [PSCustomObject]@{ Id = $upload.Id File = $detPart SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) User = $User } $fullTaskList += [PSCustomObject]@{ Id = $upload.Id File = $detPart SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) User = $User } if ($throttleCount -ge $ThrottleLimit) { $totalThrottleCount += $throttleCount if ($Wait) { Watch-GSDriveUpload -Id $taskList.Id -CountUploaded $totalThrottleCount -TotalUploading $totalFiles $throttleCount = 0 $taskList = [System.Collections.ArrayList]@() } } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } End { if (!$Wait) { $fullTaskList } else { Watch-GSDriveUpload -Id $fullTaskList.Id -CountUploaded $totalFiles -TotalUploading $totalFiles $fullStatusList = Get-GSDriveFileUploadStatus -Id $fullTaskList.Id $failedFiles = $fullStatusList | Where-Object {$_.Status -eq "Failed"} if (!$failedFiles) { Write-Verbose "All files uploaded to Google Drive successfully! Total time: $("{0:c}" -f ((Get-Date) - $start) -replace "\..*")" } elseif ($RetryCount) { $totalRetries = 0 do { $throttleCount = 0 $totalThrottleCount = 0 $taskList = [System.Collections.ArrayList]@() $fullTaskList = [System.Collections.ArrayList]@() $details = Get-Item $failedFiles.File $totalFiles = $details.Count $totalRetries++ Write-Verbose "~ ~ ~ RETRYING [$totalFiles] FAILED FILES [Retry # $totalRetries / $RetryCount] ~ ~ ~" $details = Get-Item $failedFiles.File foreach ($detPart in $details) { $throttleCount++ $contentType = Get-MimeType $detPart $body = New-Object 'Google.Apis.Drive.v3.Data.File' -Property @{ Name = [String]$detPart.Name } $parPath = "$(Split-Path $detPart.FullName -Parent)" $body.Parents = [String[]]$folIdHash[$parPath] if ($Description) { $body.Description = $Description } $stream = New-Object 'System.IO.FileStream' $detPart.FullName,'Open','Read' $request = $service.Files.Create($body,$stream,$contentType) $request.QuotaUser = $User $request.SupportsTeamDrives = $true $request.ChunkSize = 512KB $upload = $request.UploadAsync() $task = $upload.ContinueWith([System.Action[System.Threading.Tasks.Task]] {$stream.Dispose()}) Write-Verbose "[$($detPart.Name)] Upload Id $($upload.Id) has started" if (!$Script:DriveUploadTasks) { $Script:DriveUploadTasks = [System.Collections.ArrayList]@() } $script:DriveUploadTasks += [PSCustomObject]@{ Id = $upload.Id File = $detPart Length = $detPart.Length SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) StartTime = $(Get-Date) Parents = $body.Parents User = $User Upload = $upload Request = $request } $taskList += [PSCustomObject]@{ Id = $upload.Id File = $detPart SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) User = $User } $fullTaskList += [PSCustomObject]@{ Id = $upload.Id File = $detPart SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) User = $User } if ($throttleCount -ge $ThrottleLimit) { $totalThrottleCount += $throttleCount if ($Wait) { Watch-GSDriveUpload -Id $taskList.Id -CountUploaded $totalThrottleCount -TotalUploading $totalFiles -Action Retrying $throttleCount = 0 $taskList = [System.Collections.ArrayList]@() } } } Watch-GSDriveUpload -Id $fullTaskList.Id -Action Retrying -CountUploaded $totalFiles -TotalUploading $totalFiles $fullStatusList = Get-GSDriveFileUploadStatus -Id $fullTaskList.Id $failedFiles = $fullStatusList | Where-Object {$_.Status -eq "Failed"} } until (!$failedFiles -or ($totalRetries -ge $RetryCount)) if ($failedFiles) { Write-Warning "The following files failed to upload:`n`n$($failedFiles | Select-Object Id,Status,Exception,File | Format-List | Out-String)" } elseif (!$failedFiles) { Write-Verbose "All files uploaded to Google Drive successfully! Total time: $("{0:c}" -f ((Get-Date) - $start) -replace "\..*")" } } [Console]::CursorVisible = $true } } } |