我正在尝试创建一个zip文件作为目录的备份,并最终将该备份保存在该目录中的“backups”文件夹中。例如,“文件夹”包括“subFolder”,“file1.txt”,“file2.txt”和“backups”。 “backups”文件夹将包含各种早期备份文件。我想创建一个“文件夹”及其所有内容的存档,包括子文件夹,但不包括“备份”。
这是我最初想要使用的内容:
ZipFile.CreateFromDirectory(folderToZip, backupFileName);
我意识到在压缩文件夹中保存zip文件存在问题,因此我打算将其保存在其他地方,然后将其传输。但我不知道如何在没有备份文件夹的情况下轻松创建存档。我唯一的想法是以递归的方式编写我自己的方法遍历目录并排除那个文件夹。似乎必须有一种更简单的方法。
非常感谢任何帮助!
答案 0 :(得分:10)
不幸的是,ZipFile
没有提供允许您过滤条目的方法。幸运的是,您可以根据this implementation轻松创建这样的方法:
public static class ZipHelper {
public static void CreateFromDirectory(
string sourceDirectoryName
, string destinationArchiveFileName
, CompressionLevel compressionLevel
, bool includeBaseDirectory
, Encoding entryNameEncoding
, Predicate<string> filter // Add this parameter
) {
if (string.IsNullOrEmpty(sourceDirectoryName)) {
throw new ArgumentNullException("sourceDirectoryName");
}
if (string.IsNullOrEmpty(destinationArchiveFileName)) {
throw new ArgumentNullException("destinationArchiveFileName");
}
var filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories);
var entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, includeBaseDirectory);
using(var zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) {
using (var archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) {
for (int i = 0; i < filesToAdd.Length; i++) {
// Add the following condition to do filtering:
if (!filter(filesToAdd[i])) {
continue;
}
archive.CreateEntryFromFile(filesToAdd[i], entryNames[i], compressionLevel);
}
}
}
}
}
此实现允许您传递拒绝"backup/"
目录中所有条目的过滤器:
ZipHelper.CreateFromDirectory(
myDir, destFile, CompressionLevel.Fastest, true, Encoding.UTF8,
fileName => !fileName.Contains(@"\backup\")
);
答案 1 :(得分:1)
在我看来,你可以把文件夹中的每个文件都排除在&#34; backups&#34;之外。文件夹并用它们制作一个临时文件夹来制作你所说的拉链。
使用此LINQ查询,您可以以一种非常简单的方式执行此操作:
List<FileInfo> files = di.GetFiles("*", SearchOption.AllDirectories).Where(file => !file.DirectoryName.Contains("backups")).ToList();
获得此列表后,您可以复制所有这些文件,制作zip并删除内容。我看不出更简单的方法。
答案 2 :(得分:0)
您可以为ZipArchive添加扩展程序:
public static void ZipDirectory(this ZipArchive zipArchive, string srcDir, IEnumerable<Regex> excludeFileList = null, IEnumerable<Regex> excludeDirList = null , string rootDir = "")
{
if (!Directory.Exists(srcDir)) throw new Exception("source directory for zipping doesn't exit");
var dir = new DirectoryInfo(srcDir);
dir.GetFiles().ToList().ForEach((file) => {
if (excludeFileList == null || excludeFileList.Where(rule => rule.IsMatch(file.Name)).Count() == 0)
{
zipArchive.CreateEntryFromFile(file.FullName, string.IsNullOrEmpty(rootDir) ? file.Name : $@"{rootDir}\{file.Name}");
}});
dir.GetDirectories().ToList().ForEach((directory) => {
if (excludeDirList == null || excludeDirList.Where(rule => rule.IsMatch(directory.Name)).Count() == 0)
{
zipArchive.ZipDirectory(directory.FullName, excludeFileList, excludeDirList, string.IsNullOrEmpty(rootDir) ? $@"{directory.Name}" : $@"{rootDir}\{directory.Name}");
}});
}
并使用它:
var excludeFileList = new List<Regex>() { new Regex(".pdb$") };
using (var zipFileStream = new FileStream(zipFile, FileMode.Create))
{
using (var zipArch = new ZipArchive(zipFileStream, ZipArchiveMode.Create))
{
zipArch.ZipDirectory(src, excludeFileList );
}
}
答案 3 :(得分:0)
就我而言,我需要在 PowerShell 中执行此操作,这是我根据上述建议创建的脚本:
Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.FileSystem
function Get-RelativePath
{
<#
.SYNOPSIS
Returns the relative path of an object compared to a Base path
#>
param (
[Parameter(Mandatory=$true)][string]$Path,
[Parameter(Mandatory=$true)][string]$BasePath,
[Parameter(Mandatory=$true,ParameterSetName="file")][switch]$File,
[Parameter(Mandatory=$true,ParameterSetName="dir")][switch]$Directory)
$Base = [System.IO.Path]::GetFullPath($BasePath)
if ($File) {
$fi = new-object System.IO.FileInfo ($Path)
$Full = $fi.Directory
} else {
$fi = $null
$Full = new-object System.IO.DirectoryInfo ($Path)
}
$CurLoc = $Full
$Relative = @()
while ($CurLoc.FullName -ne $Base -and !([string]::IsNullOrEmpty($CurLoc.Name)))
{
#Write-Host $CurLoc.Name
$Relative += $CurLoc.Name
$CurLoc = $CurLoc.Parent
}
if ($Relative.Count -eq 0) {return ""}
[void][System.Array]::Reverse($Relative)
if ($null -ne $fi) {
$Relative += $fi.Name
}
return [string]::Join([System.IO.Path]::DirectorySeparatorChar,$Relative)
}
function Get-DirectoriesWithExclusion
{
<#
.SYNOPSIS
Gets a list ofdirectories with optional include/exclude lists
.PARAMETER SourceDirectory
Folder to compress
.PARAMETER SearchPattern
Wildcard for OS search
.PARAMETER Recurse
When enabled, will evaluate child directories too
.PARAMETER excludeDirRegex
List of regex to use in excluding Dirs by name
.PARAMETER excludeDirList
List of regex strings to use in excluding Dirs by name, only used if excludeDirRegex is not supplied
.PARAMETER includeDirRegex
List of regex to use in including Dirs by name
.PARAMETER includeDirList
List of regex strings to use in including Dirs by name, only used if includeDirRegex is not supplied
#>
param(
[Parameter(Mandatory=$true)][string]$SourceDirectory,
[string]$SearchPattern = "*",
[switch]$Recurse,
[System.Text.RegularExpressions.Regex[]]$excludeDirRegex,
[string[]]$excludeDirList,
[System.Text.RegularExpressions.Regex[]]$includeDirRegex,
[string[]]$includeDirList
)
if (![System.IO.Directory]::Exists($SourceDirectory)) { throw [System.IO.FileNotFoundException] "source directory $SourceDirectory for zipping does NOT exist";}
$di = new-object System.IO.DirectoryInfo ($SourceDirectory)
#build regex
if ($null -eq $excludeDirRegex) {
$excludeDirRegex = @()
$excludeDirRegex += $excludeDirList | Where-Object {$_}| ForEach-Object{ new-object System.Text.RegularExpressions.Regex $_,([System.Text.RegularExpressions.RegexOptions]::IgnoreCase -bor [System.Text.RegularExpressions.RegexOptions]::Compiled) }
}
if ($null -eq $includeDirRegex) {
$includeDirRegex = @()
$includeDirRegex += $includeDirList | Where-Object {$_}| ForEach-Object{ new-object System.Text.RegularExpressions.Regex $_,([System.Text.RegularExpressions.RegexOptions]::IgnoreCase -bor [System.Text.RegularExpressions.RegexOptions]::Compiled) }
}
#find the dirs recursively
foreach ($srcDir in $di.EnumerateDirectories($SearchPattern,[System.IO.SearchOption]::TopDirectoryOnly))
{
#if directory is not excluded
if (($includeDirRegex.Count -eq 0 -or ($includeDirRegex | Where-Object {$_.IsMatch($srcDir.Name)})) -and!($excludeDirRegex | Where-Object {$_.IsMatch($srcDir.Name)}))
{
Write-Output $srcDir
if ($Recurse)
{
Get-DirectoriesWithExclusion -SourceDirectory $srcDir.FullName -SearchPattern $SearchPattern -includeDirRegex $includeDirRegex -excludeDirRegex $excludeDirRegex -Recurse
}
}
}
}
function Compress-Directory
{
<#
.SYNOPSIS
Creates a zip of a directory
.DESCRIPTION
Creates a zip of a directory with optional include/exclude lists
.PARAMETER SourceDirectory
Folder to compress
.PARAMETER ZipFile
Zipfile to create
.PARAMETER SearchPattern
Wildcard for OS search
.PARAMETER Recurse
When enabled, will evaluate child directories too
.PARAMETER excludeFileRegex
List of regex to use in excluding files by name
.PARAMETER excludeFileList
List of regex strings to use in excluding files by name, only used if excludeFileRegex is not supplied
.PARAMETER excludeDirRegex
List of regex to use in excluding Dirs by name
.PARAMETER excludeDirList
List of regex strings to use in excluding Dirs by name, only used if excludeDirRegex is not supplied
.PARAMETER includeFileRegex
List of regex to use in including files by name
.PARAMETER includeFileList
List of regex strings to use in including files by name, only used if includeFileRegex is not supplied
.PARAMETER includeDirRegex
List of regex to use in including Dirs by name
.PARAMETER includeDirList
List of regex strings to use in including Dirs by name, only used if includeDirRegex is not supplied
.PARAMETER rootDirInZip
When specified will create a folder in the zip to place all files
.PARAMETER IgnoreRootFiles
When specified, files in the root of the source directory will be ignored
#>
param (
[Parameter(Mandatory=$true)][string]$SourceDirectory,
[Parameter(Mandatory=$true)][string]$ZipFile,
[string]$SearchPattern = "*",
[switch]$Recurse,
[System.Text.RegularExpressions.Regex[]]$excludeFileRegex,
[System.Text.RegularExpressions.Regex[]]$excludeDirRegex,
[string[]]$excludeFileList,
[string[]]$excludeDirList,
[System.Text.RegularExpressions.Regex[]]$includeFileRegex,
[System.Text.RegularExpressions.Regex[]]$includeDirRegex,
[string[]]$includeFileList,
[string[]]$includeDirList,
[string]$RootDirInZip,
[switch]$WhatIf,
[switch]$IgnoreRootFiles,
[System.IO.Compression.CompressionLevel]$CompressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
)
#init/validate parms
if (![System.IO.Directory]::Exists($SourceDirectory)) { throw [System.IO.FileNotFoundException] "source directory $SourceDirectory for zipping does NOT exist";}
$fi = new-object System.IO.FileInfo $ZipFile
if (!$fi.Directory.Exists) { throw [System.IO.FileNotFoundException] "ZipFile path $($fi.Directory.FullName) does NOT exist"; }
$di = new-object System.IO.DirectoryInfo ($SourceDirectory)
$sw = [System.Diagnostics.Stopwatch]::StartNew()
#build regex
if ($null -eq $excludeFileRegex) {
$excludeFileRegex = @()
$excludeFileRegex += $excludeFileList | Where-Object {$_}| ForEach-Object{ new-object System.Text.RegularExpressions.Regex $_,([System.Text.RegularExpressions.RegexOptions]::IgnoreCase -bor [System.Text.RegularExpressions.RegexOptions]::Compiled) }
}
if ($null -eq $includeFileRegex) {
$includeFileRegex = @()
$includeFileRegex += $includeFileList | Where-Object {$_}| ForEach-Object{ new-object System.Text.RegularExpressions.Regex $_,([System.Text.RegularExpressions.RegexOptions]::IgnoreCase -bor [System.Text.RegularExpressions.RegexOptions]::Compiled) }
}
$FilesZipped = 0
#build the zip
if (!($WhatIf)) {
$FileMode = [System.IO.FileMode]::OpenOrCreate
$ZipMode = ([System.IO.Compression.ZipArchiveMode]::Create)
try {
$fs = New-Object System.IO.FileStream $zipFile, $FileMode
try
{
$archive = New-Object System.IO.Compression.ZipArchive $fs,$ZipMode
} catch {
$fs.Dispose()
throw;
}
} catch {
throw;
}
}
try
{
#handle root dir
if (!($IgnoreRootFiles))
{
$RelativeRoot=""
if ($RootDirInZip) {
$RelativeRoot="$RootDirInZip\"
}
foreach ($srcFile in $di.EnumerateFiles($SearchPattern, [System.IO.SearchOption]::TopDirectoryOnly))
{
if (($includeFileRegex.Count -eq 0 -or ($includeFileRegex | Where-Object {$_.IsMatch($srcFile.Name)})) -and !($excludeFileRegex | Where-Object {$_.IsMatch($srcFile.Name)}))
{
if ($WhatIf) {
Write-Host "Would add $($srcFile.FullName) -> $RelativeRoot$($srcFile.Name)"
} else {
[void][System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive,$srcFile.FullName,"$RelativeRoot$($srcFile.Name)",$compressionLevel)
}
$FilesZipped++
}
}
}
if ($Recurse)
{
$DirList = Get-DirectoriesWithExclusion -SourceDirectory $di.FullName -SearchPattern $SearchPattern -Recurse -excludeDirList $excludeDirList -excludeDirRegex $excludeDirRegex -includeDirList $includeDirList -includeDirRegex $includeDirRegex
#handle child dirs
foreach ($srcDir in $DirList)
{
Write-Verbose "Evaluating $($srcDir.FullName) for files"
$RelativeRoot = Get-RelativePath -Directory -Path $srcDir.FullName -BasePath $SourceDirectory
if ($RootDirInZip) {
$RelativeRoot = "$RootDirInZip\$RelativeRoot\"
}
foreach ($srcFile in $srcDir.EnumerateFiles($SearchPattern,[System.IO.SearchOption]::TopDirectoryOnly))
{
if (($includeFileRegex.Count -eq 0 -or ($includeFileRegex | Where-Object {$_.IsMatch($srcFile.Name)})) -and !($excludeFileRegex | Where-Object {$_.IsMatch($srcFile.Name)}))
{
if ($WhatIf) {
Write-Host "Would add $($srcFile.FullName) -> $RelativeRoot\$($srcFile.Name)"
} else {
[void][System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive,$srcFile.FullName,"$RelativeRoot\$($srcFile.Name)",$compressionLevel)
}
}
$FilesZipped++
}
}
}
if ($WhatIf) {
Write-Host "Found $FilesZipped files to add to $ZipFile"
} else {
$fs.Flush()
$sw.Stop()
if ($FilesZipped -eq 0) {
Write-Warning "No files found to include!"
} else {
Write-Verbose "Added $FilesZipped files to $ZipFile, taking $($sw.Elapsed)"
}
}
} finally {
if (!($WhatIf)) {
$archive.Dispose()
$fs.Dispose()
}
}
}
Compress-Directory -SourceDirectory "E:\PowershellModules" -ZipFile E:\PowershellModules\test.zip -IgnoreRootFiles -RootDirInZip "Modules" -excludeFileList "log$" -includeDirList 'common' -excludeDirList "badmodule" -includeFileList "psm1$" -Verbose