如何在使用 powershell 转换、调整大小和移动图像时保持文件夹结构

How to keep folder structure while converting, resizing, and moving images using powershell

我正在尝试编写一个 powershell 脚本,它将采用源目录的参数,遍历它并将所有图像转换为 .jpg,调整它们的大小,然后将它们移动到一个新目录,同时保留它们的子目录结构。

现在我有一个脚本可以执行除维护文件夹结构之外的所有操作。

param([String]$src)
#param([String]$src, [string]$dest)
#param([String]$src, [string]$dest, [switch]$keepSubFolders)

Import-Module '\Convert-ImgageType.ps1'
Import-Module '\Set-ImageSize.ps1'

Get-ChildItem "$src\*" -include *.tif -Recurse | Convert-ImgageType -Destination $src\* -Verbose
Start-Sleep -Seconds 5
Get-ChildItem "$src\*.jpg" | Set-ImageSize -Destination $src -WidthPx 9000 -HeightPx 6000 -Verbose 
Start-Sleep -Seconds 5
Remove-Item "$src\*.jpg" -Verbose
Set-ImageSize.ps1
Function Set-ImageSize
{
    <#
    .SYNOPSIS
        Resize image file.

    .DESCRIPTION
        The Set-ImageSize cmdlet to set new size of image file.

    .PARAMETER Image
        Specifies an image file. 

    .PARAMETER Destination
        Specifies a destination of resized file. Default is current location (Get-Location).

    .PARAMETER WidthPx
        Specifies a width of image in px. 

    .PARAMETER HeightPx
        Specifies a height of image in px.      

    .PARAMETER DPIWidth
        Specifies a vertical resolution. 

    .PARAMETER DPIHeight
        Specifies a horizontal resolution.  

    .PARAMETER Overwrite
        Specifies a destination exist then overwrite it without prompt. 

    .PARAMETER FixedSize
        Set fixed size and do not try to scale the aspect ratio. 

    .PARAMETER RemoveSource
        Remove source file after conversion. 

    .EXAMPLE
        PS C:\> Get-ChildItem 'P:\test\*.jpg' | Set-ImageSize -Destination "p:\test2" -WidthPx 300 -HeightPx 375 -Verbose
        VERBOSE: Image 'P:\test[=11=]001.jpg' was resize from 236x295 to 300x375 and save in 'p:\test2[=11=]001.jpg'
        VERBOSE: Image 'P:\test[=11=]002.jpg' was resize from 236x295 to 300x375 and save in 'p:\test2[=11=]002.jpg'
        VERBOSE: Image 'P:\test[=11=]003.jpg' was resize from 236x295 to 300x375 and save in 'p:\test2[=11=]003.jpg'

    .NOTES
        Author: Michal Gajda
        Blog  : http://commandlinegeeks.com/
    #>
    [CmdletBinding(
        SupportsShouldProcess=$True,
        ConfirmImpact="Low"
    )]      
    Param
    (
        [parameter(Mandatory=$true,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true)]
        [Alias("Image")]    
        [String[]]$FullName,
        [String]$Destination = $(Get-Location),
        [Switch]$Overwrite,
        [Int]$WidthPx,
        [Int]$HeightPx,
        [Int]$DPIWidth,
        [Int]$DPIHeight,
        [Switch]$FixedSize,
        [Switch]$RemoveSource
    )
    Begin
    {
        [void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
        #[void][reflection.assembly]::loadfile( "C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll")
    }   
    Process
    {
        Foreach($ImageFile in $FullName)
        {
            If(Test-Path $ImageFile)
            {
                $OldImage = new-object System.Drawing.Bitmap $ImageFile
                $OldWidth = $OldImage.Width
                $OldHeight = $OldImage.Height               
                if($WidthPx -eq $Null)
                {
                    $WidthPx = $OldWidth
                }
                if($HeightPx -eq $Null)
                {
                    $HeightPx = $OldHeight
                }               
                if($FixedSize)
                {
                    $NewWidth = $WidthPx
                    $NewHeight = $HeightPx
                }
                else
                {
                    if($OldWidth -lt $OldHeight)
                    {
                        $NewWidth = $WidthPx
                        [int]$NewHeight = [Math]::Round(($NewWidth*$OldHeight)/$OldWidth)

                        if($NewHeight -gt $HeightPx)
                        {
                            $NewHeight = $HeightPx
                            [int]$NewWidth = [Math]::Round(($NewHeight*$OldWidth)/$OldHeight)
                        }
                    }
                    else
                    {
                        $NewHeight = $HeightPx
                        [int]$NewWidth = [Math]::Round(($NewHeight*$OldWidth)/$OldHeight)

                        if($NewWidth -gt $WidthPx)
                        {
                            $NewWidth = $WidthPx
                            [int]$NewHeight = [Math]::Round(($NewWidth*$OldHeight)/$OldWidth)
                        }                       
                    }
                }
                $ImageProperty = Get-ItemProperty $ImageFile                
                $SaveLocation = Join-Path -Path $Destination -ChildPath ($ImageProperty.Name)
                If(!$Overwrite)
                {
                    If(Test-Path $SaveLocation)
                    {
                        $Title = "A file already exists: $SaveLocation"                         
                        $ChoiceOverwrite = New-Object System.Management.Automation.Host.ChoiceDescription "&Overwrite"
                        $ChoiceCancel = New-Object System.Management.Automation.Host.ChoiceDescription "&Cancel"
                        $Options = [System.Management.Automation.Host.ChoiceDescription[]]($ChoiceCancel, $ChoiceOverwrite)     
                        If(($host.ui.PromptForChoice($Title, $null, $Options, 1)) -eq 0)
                        {
                            Write-Verbose "Image '$ImageFile' exist in destination location - skiped"
                            Continue
                        } #End If ($host.ui.PromptForChoice($Title, $null, $Options, 1)) -eq 0
                    } #End If Test-Path $SaveLocation
                } #End If !$Overwrite                   
                $NewImage = new-object System.Drawing.Bitmap $NewWidth,$NewHeight
                $Graphics = [System.Drawing.Graphics]::FromImage($NewImage)
                $Graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
                $Graphics.DrawImage($OldImage, 0, 0, $NewWidth, $NewHeight) 

                $ImageFormat = $OldImage.RawFormat
                $OldImage.Dispose()
                if($DPIWidth -and $DPIHeight)
                {
                    $NewImage.SetResolution($DPIWidth,$DPIHeight)
                } #End If $DPIWidth -and $DPIHeight

                $NewImage.Save($SaveLocation,$ImageFormat)
                $NewImage.Dispose()
                Write-Verbose "Image '$ImageFile' was resize from $($OldWidth)x$($OldHeight) to $($NewWidth)x$($NewHeight) and save in '$SaveLocation'"             
                If($RemoveSource)
                {
                    Remove-Item $Image -Force
                    Write-Verbose "Image source '$ImageFile' was removed"
                } #End If $RemoveSource
            }
        }
    } #End Process  
    End{}
}

为了让它做你想做的事,即在目标目录中保留源图像的文件夹结构,我编辑了 Set-ImageSize 函数。 它现在有一个名为 $SourcePath.
的额外(可选)参数 使用它,可以确定目的地的文件夹结构。 由于 $SourcePath 参数是可选的,在函数调用中不使用该参数,因此调整大小后的图像都保存在同一个 Destination 文件夹中。

这也意味着您需要稍微更改函数的调用以实现这一点。 首先,您需要确保 -Destination 指向文件源以外的其他文件夹。确实没什么新鲜事,但那是你最初犯的错误。

总之先适配函数

function Set-ImageSize {
    <#
    .SYNOPSIS
        Resize image file.

    .DESCRIPTION
        The Set-ImageSize cmdlet to set new size of image file.

    .PARAMETER FullName
        Specifies an image file. 

    .PARAMETER SourcePath
        Optional. If used, the function creates the same folder structure in the Destination path
        to save the resized images. When not used, the resized images are all saved in the Destination folder.

    .PARAMETER Destination
        Specifies a destination of resized file. Default is current location (Get-Location).

    .PARAMETER WidthPx
        Specifies a width of image in px. 

    .PARAMETER HeightPx
        Specifies a height of image in px.      

    .PARAMETER DPIWidth
        Specifies a vertical resolution. 

    .PARAMETER DPIHeight
        Specifies a horizontal resolution.  

    .PARAMETER Overwrite
        Specifies a destination exist then overwrite it without prompt. 

    .PARAMETER FixedSize
        Set fixed size and do not try to scale the aspect ratio. 

    .PARAMETER RemoveSource
        Remove source file after conversion. 

    .EXAMPLE
        PS C:\> Get-ChildItem 'P:\test\*.jpg' | Set-ImageSize -Destination "p:\test2" -WidthPx 300 -HeightPx 375 -Verbose
        VERBOSE: Image 'P:\test[=10=]001.jpg' was resize from 236x295 to 300x375 and save in 'p:\test2[=10=]001.jpg'
        VERBOSE: Image 'P:\test[=10=]002.jpg' was resize from 236x295 to 300x375 and save in 'p:\test2[=10=]002.jpg'
        VERBOSE: Image 'P:\test[=10=]003.jpg' was resize from 236x295 to 300x375 and save in 'p:\test2[=10=]003.jpg'

    .NOTES
        Author: Michal Gajda
        Edited: Theo
        Blog  : http://commandlinegeeks.com/
    #>
    [CmdletBinding(
        SupportsShouldProcess=$True,
        ConfirmImpact="Low"
    )]      
    Param
    (
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [Alias("Image")]    
        [String[]]$FullName,
        [string]$SourcePath = $null,
        [String]$Destination = $(Get-Location),
        [Switch]$Overwrite,
        [Int]$WidthPx,
        [Int]$HeightPx,
        [Int]$DPIWidth,
        [Int]$DPIHeight,
        [Switch]$FixedSize,
        [Switch]$RemoveSource
    )
    Begin {
        Add-Type -AssemblyName System.Windows.Forms
        Add-Type -AssemblyName System.Drawing
    }   
    Process {
        foreach($ImageFile in $FullName) {
            if (Test-Path $ImageFile -PathType Leaf) {
                $OldImage = New-Object System.Drawing.Bitmap $ImageFile
                $OldWidth = $OldImage.Width
                $OldHeight = $OldImage.Height               
                if (!$WidthPx) { $WidthPx = $OldWidth }
                if (!$HeightPx) { $HeightPx = $OldHeight }               
                if ($FixedSize) {
                    $NewWidth  = $WidthPx
                    $NewHeight = $HeightPx
                }
                else {
                    if($OldWidth -lt $OldHeight) {
                        $NewWidth = $WidthPx
                        [int]$NewHeight = [Math]::Round(($NewWidth*$OldHeight)/$OldWidth)
                        if($NewHeight -gt $HeightPx) {
                            $NewHeight = $HeightPx
                            [int]$NewWidth = [Math]::Round(($NewHeight*$OldWidth)/$OldHeight)
                        }
                    }
                    else {
                        $NewHeight = $HeightPx
                        [int]$NewWidth = [Math]::Round(($NewHeight*$OldWidth)/$OldHeight)
                        if($NewWidth -gt $WidthPx) {
                            $NewWidth = $WidthPx
                            [int]$NewHeight = [Math]::Round(($NewWidth*$OldHeight)/$OldWidth)
                        }                       
                    }
                }
                # most changes made here
                $imageFileName = [System.IO.Path]::GetFileName($ImageFile)

                if ([string]::IsNullOrWhiteSpace($SourcePath)) {
                    # save in the destination path
                    $SaveFilePath = $Destination
                    $SaveFileName = Join-Path -Path $Destination -ChildPath $imageFileName
                }
                else {
                    # keep directory structure
                    $imageFilePath = [System.IO.Path]::GetDirectoryName($ImageFile).Substring($SourcePath.Length)
                    $SaveFilePath  = Join-Path -Path $Destination -ChildPath $imageFilePath
                    $SaveFileName  = Join-Path -Path $SaveFilePath -ChildPath $imageFileName
                }

                # create the destination in $SaveFilePath if it does not already exist
                if (!(Test-Path -Path $SaveFilePath)) {
                    New-Item -Path $SaveFilePath -ItemType Directory | Out-Null
                }

                if (!$Overwrite) {
                    if (Test-Path $SaveFileName -PathType Leaf) {
                        # are you sure you want to get this confirmation dialog every time?
                        $Title = "A file already exists: $SaveFileName"                         
                        $ChoiceOverwrite = New-Object System.Management.Automation.Host.ChoiceDescription "&Overwrite"
                        $ChoiceCancel    = New-Object System.Management.Automation.Host.ChoiceDescription "&Cancel"
                        $Options = [System.Management.Automation.Host.ChoiceDescription[]]($ChoiceCancel, $ChoiceOverwrite)     
                        if (($host.ui.PromptForChoice($Title, $null, $Options, 1)) -eq 0) {
                            Write-Verbose "Image '$ImageFile' already exist in destination location - skipped"
                            Continue
                        }
                    }
                }                  
                $NewImage = New-Object System.Drawing.Bitmap $NewWidth,$NewHeight
                $Graphics = [System.Drawing.Graphics]::FromImage($NewImage)
                $Graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
                $Graphics.DrawImage($OldImage, 0, 0, $NewWidth, $NewHeight) 

                $ImageFormat = $OldImage.RawFormat
                $OldImage.Dispose()

                if ($DPIWidth -and $DPIHeight){
                    $NewImage.SetResolution($DPIWidth,$DPIHeight)
                }

                $NewImage.Save($SaveFileName, $ImageFormat)
                $NewImage.Dispose()

                Write-Verbose "Image '$ImageFile' was resized from $($OldWidth)x$($OldHeight) to $($NewWidth)x$($NewHeight) and saved as '$SaveFileName'"             
                if ($RemoveSource) {
                    Remove-Item $ImageFile -Force
                    Write-Verbose "Image source file '$ImageFile' was removed"
                }
            }
        }
    }
    End{}
}

现在函数调用:

param([String]$src, [string]$dest, [switch]$keepSubFolders)

Import-Module '\Convert-ImgageType.ps1'
Import-Module '\Set-ImageSize.ps1'

Get-ChildItem "$src\*" -include *.tif -Recurse | Convert-ImgageType -Destination $src\* -Verbose
Start-Sleep -Seconds 5

if ($keepSubFolders) {
    # to keep the folder structure, you need to use the -SourcePath parameter
    Get-ChildItem "$src\*.jpg" -File -Recurse | Set-ImageSize -SourcePath $src -Destination $dest -WidthPx 9000 -HeightPx 6000 -Verbose
}
else {
    Get-ChildItem "$src\*.jpg" -File -Recurse | Set-ImageSize -Destination $dest -WidthPx 9000 -HeightPx 6000 -Verbose
}

Start-Sleep -Seconds 5
# you could also use the -RemoveSource switch on the Set-ImageSize function..
Remove-Item "$src\*.jpg" -Verbose

注:Convert-ImgageType函数我没有测试过..