2013-02-28 142 views
17

ZipArchive是ZipArchiveEntries的集合,添加/删除“条目”效果很好。 但它似乎没有目录/嵌套“档案”的概念。理论上讲,这个类与文件系统是分离的,因为你可以完全在一个内存流中创建归档。但是,如果您希望在存档中添加目录结构,则必须在条目名称前添加一个路径。在ZipArchive中创建目录C#.Net 4.5

问题:你将如何去扩展ZipArchive创建一个更好的界面来创建和管理目录?

例如,将一个文件添加到一个目录电流的方法是创建的目录路径条目:

var entry = _archive.CreateEntry("directory/entryname"); 

而这些方针的东西看起来更好的对我说:

var directory = _archive.CreateDirectoryEntry("directory"); 
var entry = _directory.CreateEntry("entryname"); 
+0

你的意思是一个zip文件夹或一个zip层次结构的文件夹结构吗? – 2013-02-28 10:56:51

+0

单个zip内的文件夹结构。 – 2013-02-28 11:01:16

回答

27

您可以使用类似于下面的东西,换句话说,手动创建的目录结构:

using (var fs = new FileStream("1.zip", FileMode.Create)) 
using (var zip = new ZipArchive(fs, ZipArchiveMode.Create)) 
{ 
    zip.CreateEntry("12/3/"); // just end with "/" 
} 
6

这里是一个可能的解决方案:

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); 
    } 
} 

,并用于:

var directory = _archive.CreateDirectory(context); 
var entry = directory.CreateEntry(context); 
var stream = entry.Open(); 

但我可以预见到嵌套问题,也许。

6

如果你在一个项目,可以使用完整的.NET工作,你可以尝试使用 的ZipFile.CreateFromDirectory方法,如explained here

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); 
     } 
    } 
} 

当然,如果你正在创建基于给定的目录新的拉链,这只会工作。

根据评论,以前的解决方案不保留目录结构。如果需要,那么下面的代码可能会解决这个问题:

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); 
     } 
    } 
+3

这只是一个迂腐的笔记,我发现一些摆弄后的东西。在Windows桌面上,它不能正常工作*作为标准的“发送到 - >压缩文件”。 Windows SendTo将显式地为结构中的目录创建条目,其中该方法隐式地维护目录结构,即它不为目录创建条目,但目录在每个文件的完整路径中列出。它不会改变winzip函数的工作方式(很高兴能与其中任何一个协同工作),但它只是意识到是否需要特定的文件结构。 – 2017-03-08 08:53:58

+0

谢谢。为我节省了很多时间。我只是对代码做了一个小小的修改,希望你能接受它。我添加了Substring(1)来获取相对路径,以使zip文件可浏览。 – 2017-10-31 08:26:51

0

使用递归方法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 + "/"); 
    } 
}