Powershell压缩文件夹内容

8

我知道有很多关于使用PowerShell压缩文件的内容,但我找不到完全符合我需求的方法。

我想能够将文件夹和文件压缩成一个.zip文件,而不需要在压缩包中包含父文件夹。例如,我有一个名为STUFF的文件夹,其中包含文件/文件夹,我想将其压缩到一个名为STUFF.zip的文件夹中。这个文件夹的结构将是STUFF.zip>files/folders,而不是像我目前使用的代码一样是STUFF.zip>STUFF>files/folders。

function CountZipItems(
[__ComObject] $zipFile)
{
If ($zipFile -eq $null)
{
    Throw "Value cannot be null: zipFile"
}

Write-Host ("Counting items in zip file (" + $zipFile.Self.Path + ")...")

[int] $count = CountZipItemsRecursive($zipFile)

Write-Host ($count.ToString() + " items in zip file (" `
    + $zipFile.Self.Path + ").")

return $count
}

function CountZipItemsRecursive(
[__ComObject] $parent)
{
If ($parent -eq $null)
{
    Throw "Value cannot be null: parent"
}

[int] $count = 0

$parent.Items() |
    ForEach-Object {
        $count += 1

        If ($_.IsFolder -eq $true)
        {
            $count += CountZipItemsRecursive($_.GetFolder)
        }
    }

return $count
}

function IsFileLocked(
[string] $path)
{
If ([string]::IsNullOrEmpty($path) -eq $true)
{
    Throw "The path must be specified."
}

[bool] $fileExists = Test-Path $path

If ($fileExists -eq $false)
{
    Throw "File does not exist (" + $path + ")"
}

[bool] $isFileLocked = $true

$file = $null

Try
{
    $file = [IO.File]::Open(
        $path,
        [IO.FileMode]::Open,
        [IO.FileAccess]::Read,
        [IO.FileShare]::None)

    $isFileLocked = $false
}
Catch [IO.IOException]
{
    If ($_.Exception.Message.EndsWith(
        "it is being used by another process.") -eq $false)
    {
        Throw $_.Exception
    }
}
Finally
{
    If ($file -ne $null)
    {
        $file.Close()
    }
}

return $isFileLocked
}

function GetWaitInterval(
[int] $waitTime)
{
If ($waitTime -lt 1000)
{
    return 100
}
ElseIf ($waitTime -lt 5000)
{
    return 1000
}
Else
{
    return 5000
}
}

function WaitForZipOperationToFinish(
[__ComObject] $zipFile,
[int] $expectedNumberOfItemsInZipFile)
{
If ($zipFile -eq $null)
{
    Throw "Value cannot be null: zipFile"
}
ElseIf ($expectedNumberOfItemsInZipFile -lt 1)
{
    Throw "The expected number of items in the zip file must be specified."
}

Write-Host -NoNewLine "Waiting for zip operation to finish..."
Start-Sleep -Milliseconds 1000 # ensure zip operation had time to start

[int] $waitTime = 0
[int] $maxWaitTime = 60 * 1000 # [milliseconds]
while($waitTime -lt $maxWaitTime)
{
    [int] $waitInterval = GetWaitInterval($waitTime)

    Write-Host -NoNewLine "."
    Start-Sleep -Milliseconds $waitInterval
    $waitTime += $waitInterval

    Write-Debug ("Wait time: " + $waitTime / 1000 + " seconds")

    [bool] $isFileLocked = IsFileLocked($zipFile.Self.Path)

    If ($isFileLocked -eq $true)
    {
        Write-Debug "Zip file is locked by another process."
        Continue
    }
    Else
    {
        Break
    }
}

Write-Host                           

If ($waitTime -ge $maxWaitTime)
{
    Throw "Timeout exceeded waiting for zip operation"
}

[int] $count = CountZipItems($zipFile)

If ($count -eq $expectedNumberOfItemsInZipFile)
{
    Write-Debug "The zip operation completed succesfully."
}
ElseIf ($count -eq 0)
{
    Throw ("Zip file is empty. This can occur if the operation is" `
        + " cancelled by the user.")
}
ElseIf ($count -gt $expectedCount)
{
    Throw "Zip file contains more than the expected number of items."
}
}

function ZipFolder(
[IO.DirectoryInfo] $directory)
{
If ($directory -eq $null)
{
    Throw "Value cannot be null: directory"
}

Write-Host ("Creating zip file for folder (" + $directory.FullName + ")...")

[IO.DirectoryInfo] $parentDir = $directory.Parent

[string] $zipFileName

If ($parentDir.FullName.EndsWith("\") -eq $true)
{
    # e.g. $parentDir = "C:\"
    $zipFileName = $parentDir.FullName + $directory.Name + ".zip"
}
Else
{
    $zipFileName = $parentDir.FullName + "\" + $directory.Name + ".zip"
    #$zipFileName = $directory.Name + ".zip"
    #$zipFileName = $parentDir.FullName + ".zip"
}

If (Test-Path $zipFileName)
{
    Throw "Zip file already exists ($zipFileName)."
}

Set-Content $zipFileName ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))

$shellApp = New-Object -ComObject Shell.Application
$zipFile = $shellApp.NameSpace($zipFileName)

If ($zipFile -eq $null)
{
    Throw "Failed to get zip file object."
}

[int] $expectedCount = (Get-ChildItem $directory -Force -Recurse).Count
$expectedCount += 1 # account for the top-level folder

$zipFile.CopyHere($directory.FullName)
#Get-ChildItem $directory | foreach {$zipFile.CopyHere($_.fullname)}

# wait for CopyHere operation to complete
WaitForZipOperationToFinish $zipFile $expectedCount

Write-Host -Fore Green ("Successfully created zip file for folder (" `
    + $directory.FullName + ").")
}

使用方法

Remove-Item "H:\STUFF.zip"

[IO.DirectoryInfo] $directory = Get-Item "H:\STUFF"
ZipFolder $directory

这段代码的完整来源在这里。非常感谢任何帮助,这个功能对我的项目至关重要!不幸的是,我不能使用Community Extension模块,因为其他计算机没有安装此模块。

谢谢!


Dotnet 4.5添加了zip功能,应该比COM方法提供更好的选项。其他电脑有吗? - mjolinor
4个回答

17

根据 @mjolinor 的说法 "H:\Stuff\",如果您已安装 .NET 4.5,则可以运行以下内容:

$src = "H:\Stuff\"
$dst = "H:\Stuff.zip"
[Reflection.Assembly]::LoadWithPartialName( "System.IO.Compression.FileSystem" )
[System.IO.Compression.ZipFile]::CreateFromDirectory($src, $dst)

7

在WMF 5中提供了新的解决方案(其实不是很新 ^^)

Compress-Archive -Path H:\stuff\* -DestinationPath H:\stuff.zip -Force

6
如果您使用 PSCX 模块,您将使用下面的代码行来获得您需要的内容:
write-zip h:\Stuff\* myzipfile.zip 

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接