在 ZipArchive C# .Net 4.5 中创建目录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15133626/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating Directories in a ZipArchive C# .Net 4.5
提问by Meirion Hughes
A ZipArchive is a collection of ZipArchiveEntries, and adding/removing "Entries" works nicely. But it appears there is no notion of directories / nested "Archives". In theory, the class is decoupled from a file system, in that you can create the archive completely in a memory stream. But if you wish to add a directory structure within the archive, you must prefix the entry name with a path.
ZipArchive 是 ZipArchiveEntries 的集合,添加/删除“条目”效果很好。但似乎没有目录/嵌套“档案”的概念。理论上,该类与文件系统分离,因为您可以完全在内存流中创建存档。但是,如果您希望在存档中添加目录结构,则必须在条目名称前加上路径。
Question:How would you go about extending ZipArchive to create a better interface for creating and managing directories?
问题:您将如何扩展 ZipArchive 以创建更好的界面来创建和管理目录?
For example, the current method of adding a file to a directory is to create the entry with the directory path:
例如,当前将文件添加到目录的方法是使用目录路径创建条目:
var entry = _archive.CreateEntry("directory/entryname");
whereas something along these lines seems nicer to me:
而沿着这些路线的东西对我来说似乎更好:
var directory = _archive.CreateDirectoryEntry("directory");
var entry = _directory.CreateEntry("entryname");
采纳答案by Aimeast
You can use something like the following, in other words, create the directory structure by hand:
您可以使用以下内容,换句话说,手动创建目录结构:
using (var fs = new FileStream("1.zip", FileMode.Create))
using (var zip = new ZipArchive(fs, ZipArchiveMode.Create))
{
zip.CreateEntry("12/3/"); // just end with "/"
}
回答by Meirion Hughes
Here is one possible solution:
这是一种可能的解决方案:
public static class ZipArchiveExtension
{
public static ZipArchiveDirectory CreateDirectory(this ZipArchive @this, string directoryPath)
{
return new ZipArchiveDirectory(@this, directoryPath);
}
}
public class ZipArchiveDirectory
{
private readonly string _directory;
private ZipArchive _archive;
internal ZipArchiveDirectory(ZipArchive archive, string directory)
{
_archive = archive;
_directory = directory;
}
public ZipArchive Archive { get{return _archive;}}
public ZipArchiveEntry CreateEntry(string entry)
{
return _archive.CreateEntry(_directory + "/" + entry);
}
public ZipArchiveEntry CreateEntry(string entry, CompressionLevel compressionLevel)
{
return _archive.CreateEntry(_directory + "/" + entry, compressionLevel);
}
}
and used:
并使用:
var directory = _archive.CreateDirectory(context);
var entry = directory.CreateEntry(context);
var stream = entry.Open();
but I can foresee problems with nesting, perhaps.
但我可以预见嵌套问题,也许。
回答by hmadrigal
If you are working on a project that can use full .NET you may try to use the ZipFile.CreateFromDirectory method, as explained here:
如果您正在处理可以使用完整 .NET 的项目,您可以尝试使用 ZipFile.CreateFromDirectory 方法,如下所述:
using System;
using System.IO;
using System.IO.Compression;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
string startPath = @"c:\example\start";
string zipPath = @"c:\example\result.zip";
string extractPath = @"c:\example\extract";
ZipFile.CreateFromDirectory(startPath, zipPath, CompressionLevel.Fastest, true);
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
}
Of course this will only work if you are creating new Zips based on a given directory.
当然,这仅在您基于给定目录创建新 Zip 时才有效。
As per the comment, the previous solution does not preserve the directory structure. If that is needed, then the following code might address that:
根据评论,以前的解决方案不保留目录结构。如果需要,那么以下代码可能会解决这个问题:
var InputDirectory = @"c:\example\start";
var OutputFilename = @"c:\example\result.zip";
using (Stream zipStream = new FileStream(Path.GetFullPath(OutputFilename), FileMode.Create, FileAccess.Write))
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
{
foreach(var filePath in System.IO.Directory.GetFiles(InputDirectory,"*.*",System.IO.SearchOption.AllDirectories))
{
var relativePath = filePath.Replace(InputDirectory,string.Empty);
using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (Stream fileStreamInZip = archive.CreateEntry(relativePath).Open())
fileStream.CopyTo(fileStreamInZip);
}
}
回答by Hack ok
Use the recursive approach to Zip Folders with Subfolders.
对带有子文件夹的 Zip 文件夹使用递归方法。
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
public static async Task<bool> ZipFileHelper(IFolder folderForZipping, IFolder folderForZipFile, string zipFileName)
{
if (folderForZipping == null || folderForZipFile == null
|| string.IsNullOrEmpty(zipFileName))
{
throw new ArgumentException("Invalid argument...");
}
IFile zipFile = await folderForZipFile.CreateFileAsync(zipFileName, CreationCollisionOption.ReplaceExisting);
// Create zip archive to access compressed files in memory stream
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
await ZipSubFolders(folderForZipping, zip, "");
}
zipStream.Position = 0;
using (Stream s = await zipFile.OpenAsync(FileAccess.ReadAndWrite))
{
zipStream.CopyTo(s);
}
}
return true;
}
//Create zip file entry for folder and subfolders("sub/1.txt")
private static async Task ZipSubFolders(IFolder folder, ZipArchive zip, string dir)
{
if (folder == null || zip == null)
return;
var files = await folder.GetFilesAsync();
var en = files.GetEnumerator();
while (en.MoveNext())
{
var file = en.Current;
var entry = zip.CreateEntryFromFile(file.Path, dir + file.Name);
}
var folders = await folder.GetFoldersAsync();
var fEn = folders.GetEnumerator();
while (fEn.MoveNext())
{
await ZipSubFolders(fEn.Current, zip, dir + fEn.Current.Name + "/");
}
}
回答by Val
I know I'm late to the party (7.25.2018),
我知道我迟到了 (7.25.2018),
this works flawlessly to me, even with recursive directories.
这对我来说完美无缺,即使是递归目录。
Firstly, remember to install the NuGet package:
首先,记得安装 NuGet 包:
Install-Package System.IO.Compression
Install-Package System.IO.Compression
And then, Extension file for ZipArchive
:
然后,扩展文件为ZipArchive
:
public static class ZipArchiveExtension {
public static void CreateEntryFromAny(this ZipArchive archive, string sourceName, string entryName = "") {
var fileName = Path.GetFileName(sourceName);
if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory)) {
archive.CreateEntryFromDirectory(sourceName, Path.Combine(entryName, fileName));
} else {
archive.CreateEntryFromFile(sourceName, Path.Combine(entryName, fileName), CompressionLevel.Fastest);
}
}
public static void CreateEntryFromDirectory(this ZipArchive archive, string sourceDirName, string entryName = "") {
string[] files = Directory.GetFiles(sourceDirName).Concat(Directory.GetDirectories(sourceDirName)).ToArray();
archive.CreateEntry(Path.Combine(entryName, Path.GetFileName(sourceDirName)));
foreach (var file in files) {
archive.CreateEntryFromAny(file, entryName);
}
}
}
And then you can pack anything, whether it is file or directory:
然后你可以打包任何东西,无论是文件还是目录:
using (var memoryStream = new MemoryStream()) {
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
archive.CreateEntryFromAny(sourcePath);
}
}
回答by sDima
My answer is based on the Val's answer, but a little improved for performance and without producing empty files in ZIP.
我的回答基于 Val 的回答,但性能有所提高,并且不会在 ZIP 中生成空文件。
public static class ZipArchiveExtensions
{
public static void CreateEntryFromAny(this ZipArchive archive, string sourceName, string entryName = "")
{
var fileName = Path.GetFileName(sourceName);
if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory))
{
archive.CreateEntryFromDirectory(sourceName, Path.Combine(entryName, fileName));
}
else
{
archive.CreateEntryFromFile(sourceName, Path.Combine(entryName, fileName), CompressionLevel.Optimal);
}
}
public static void CreateEntryFromDirectory(this ZipArchive archive, string sourceDirName, string entryName = "")
{
var files = Directory.EnumerateFileSystemEntries(sourceDirName);
foreach (var file in files)
{
archive.CreateEntryFromAny(file, entryName);
}
}
}
Example of using:
使用示例:
// Create and open a new ZIP file
using (var zip = ZipFile.Open(ZipPath, ZipArchiveMode.Create))
{
foreach (string file in FILES_LIST)
{
// Add the entry for each file
zip.CreateEntryFromAny(file);
}
}
回答by Nitheesh George
I was also looking for a similar solution and found @Val & @sDima's solution more promising to me. But I found some issues with the code and fixed them to use with my code.
我也在寻找类似的解决方案,发现 @Val 和 @sDima 的解决方案对我更有希望。但是我发现代码存在一些问题并修复了它们以与我的代码一起使用。
Like @sDima, I also decided to use Extension to add more functionality to ZipArchive.
和@sDima 一样,我也决定使用 Extension 为 ZipArchive 添加更多功能。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Compression;
using System.IO;
namespace ZipTest
{
public static class ZipArchiveExtensions
{
public static void CreateEntryFromAny(this ZipArchive archive, string sourceName, string entryName, CompressionLevel compressionLevel = CompressionLevel.Optimal)
{
try
{
if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory))
{
archive.CreateEntryFromDirectory(sourceName, entryName, compressionLevel);
}
else
{
archive.CreateEntryFromFile(sourceName, entryName, compressionLevel);
}
}
catch
{
throw;
}
}
public static void CreateEntryFromDirectory(this ZipArchive archive, string sourceDirName, string entryName, CompressionLevel compressionLevel)
{
try
{
var files = Directory.EnumerateFileSystemEntries(sourceDirName);
if (files.Any())
{
foreach (var file in files)
{
var fileName = Path.GetFileName(file);
archive.CreateEntryFromAny(file, Path.Combine(entryName, fileName), compressionLevel);
}
}
else
{
//Do a folder entry check.
if (!string.IsNullOrEmpty(entryName) && entryName[entryName.Length - 1] != '/')
{
entryName += "/";
}
archive.CreateEntry(entryName, compressionLevel);
}
}
catch
{
throw;
}
}
}
}
For invoking use the following way,
对于调用使用以下方式,
class Program
{
static void Main(string[] args)
{
string filePath = @"C:\Users\WinUser\Downloads\Test.zip";
string dirName = Path.GetDirectoryName(filePath);
if (File.Exists(filePath))
File.Delete(filePath);
using (ZipArchive archive = ZipFile.Open(filePath, ZipArchiveMode.Create))
{
archive.CreateEntryFromFile( @"C:\Users\WinUser\Downloads\file1.jpg", "SomeFolder/file1.jpg", CompressionLevel.Optimal);
archive.CreateEntryFromDirectory(@"C:\Users\WinUser\Downloads\MyDocs", "OfficeDocs", CompressionLevel.Optimal);
archive.CreateEntryFromAny(@"C:\Users\WinUser\Downloads\EmptyFolder", "EmptyFolder", CompressionLevel.Optimal);
};
using (ZipArchive zip = ZipFile.OpenRead(filePath))
{
string dirExtract = @"C:\Users\WinUser\Downloads\Temp";
if (Directory.Exists(dirExtract))
{
Directory.Delete(dirExtract, true);
}
zip.ExtractToDirectory(dirExtract);
}
}
}
I tried to keep the exact behavior of standard CreateEntryFromFilefrom the .Net Frame for extension methods.
我试图保留标准 CreateEntryFromFilefrom the .Net Frame for extension methods 的确切行为。
To use the sample code given, add reference to System.IO.Compression.dll and System.IO.Compression.FileSystem.dll.
要使用给出的示例代码,请添加对 System.IO.Compression.dll 和 System.IO.Compression.FileSystem.dll 的引用。
Following are advantages of this extension class
以下是此扩展类的优点
- Recursive adding of folder content.
- Support for an empty folder.
- 递归添加文件夹内容。
- 支持空文件夹。