2013-02-28 141 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 + "/"); 
    } 
}