当目录在.zip存档时如何获取目录信息?

问题描述:

我的路径是C:\用户\ XX \桌面\文件夹\ E_sa_sub.zip \ E_sa_sub \ subbb
问题是E_sa_sub.zip当目录在.zip存档时如何获取目录信息?

当我试图DirectoryInfo.GetDirectories(),我得到了错误“无法找到的一部分路径”

List<DirectoryInfo> arr = new List<DirectoryInfo>(); 

    private void SubFoldersFiles(string path) 
    { 
     DirectoryInfo dInfo = new DirectoryInfo(path); 
     foreach (DirectoryInfo d in dInfo.GetDirectories()) 
     { 
      SubFoldersFiles(d.FullName); 
      arr.Add(d); 
     } 
    } 
+0

是'C:\ Users \ xx \ Desktop \ Folder \ E_sa_sub.zip'文件还是目录? 'GetDirectories()'不可能在'.zip'文件中寻找路径。如果'C:\ Users \ xx \ Desktop \ Folder \ E_sa_sub.zip \ E_sa_sub \ subbb问题是E_sa_sub.zip'是'.zip'文件,那么您需要传递目录'C:\ Users \ xx \ Desktop \ Folder \ E_sa_sub.zip \ E_sa_sub \',例如通过使用'new DirectoryInfo(Path.GetDirectoryName(path));'请给我们目录结构。 –

一个.zip档案文件并不是一个真正的Windows文件系统的目录,你正在试图做的,根本不会工作,所以什么。另外,.zip存档文件甚至没有真正的目录。它具有名称的存档条目,其中这些名称可以包含路径分隔符字符(不像Windows文件系统上的路径,如NTFS或FAT)。

如果名称中包含路径分隔符,则归档操作工具(如Windows的内置.zip处理功能或像7zip这样的程序将条目名称视为包含完整的目录路径),并使用路径分隔符字符确定存储在真实文件系统中时条目的虚拟路径。

对于Windows资源管理器(即Windows的GUI外壳),当您双击一个.zip存档文件时,它将打开它并在与常规文件系统窗口完全相同的窗口中显示内容。但事实并非如此,您不能使用像DirectoryFile这样的常用文件系统导航类来访问它。

如果您想将存档视为其中包含目录,则由您来检查存档中的所有条目名称并在存储器中构建一个表示目录结构的数据结构(例如树)隐含在档案中的所有条目中。您可以通过一次构建整个树来完成此任务,也可以只查看每个条目名称的第一个路径组件,并将其视为归档根目录中文件项没有任何路径的项目分隔符字符,而目录项至少有一个。

通过递归地解析条目名称,您可以按照这种方式导航.zip存档。

或者,只需使用ExtractToDirectory()方法将.zip存档内容临时复制到实际文件系统上的某个位置,然后就可以在那里使用常规文件系统导航类。

这里是我上面描述的第二类实现的一个例子。即给定的起始路径,它将处理归档输入和识别单个虚拟目录和/或文件项,在通过该路径指定的目录有效是:

[Flags] 
public enum EntryInfoTypes 
{ 
    Directory = 1, 
    File = 2, 
    DirectoryOrFile = Directory | File 
} 

public static class ZipDirectoryExtensions 
{ 
    private static readonly char[] _pathSeparators = 
     new[] { Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar }; 

    public static IEnumerable<ZipDirectoryInfo> EnumerateDirectories(
     this ZipArchive archive, string path) 
    { 
     return archive.EnumerateDirectories(path, SearchOption.TopDirectoryOnly); 
    } 

    public static IEnumerable<ZipDirectoryInfo> EnumerateDirectories(
     this ZipArchive archive, string path, SearchOption searchOption) 
    { 
     return archive.EnumerateEntryInfos(path, searchOption, EntryInfoTypes.Directory) 
      .Cast<ZipDirectoryInfo>(); 
    } 

    public static IEnumerable<ZipFileInfo> EnumerateFiles(
     this ZipArchive archive, string path) 
    { 
     return archive.EnumerateFiles(path, SearchOption.TopDirectoryOnly); 
    } 

    public static IEnumerable<ZipFileInfo> EnumerateFiles(
     this ZipArchive archive, string path, SearchOption searchOption) 
    { 
     return archive.EnumerateEntryInfos(path, searchOption, EntryInfoTypes.File) 
      .Cast<ZipFileInfo>(); 
    } 

    public static IEnumerable<ZipEntryInfo> EnumerateEntryInfos(
     this ZipArchive archive, string path, EntryInfoTypes entryInfoTypes) 
    { 
     return archive.EnumerateEntryInfos(
      path, SearchOption.TopDirectoryOnly, entryInfoTypes); 
    } 

    public static IEnumerable<ZipEntryInfo> EnumerateEntryInfos(this ZipArchive archive, 
     string path, SearchOption searchOption, EntryInfoTypes entryInfoTypes) 
    { 
     // Normalize input path, by removing any path separator character from the 
     // beginning, and ensuring one is present at the end. This will ensure that 
     // the path variable format matches the format used in the archive and which 
     // is also convenient for the implementation of the algorithm below. 
     if (path.Length > 0) 
     { 
      if (_pathSeparators.Contains(path[0])) 
      { 
       path = path.Substring(1); 
      } 

      if (!_pathSeparators.Contains(path[path.Length - 1])) 
      { 
       path = path + Path.AltDirectorySeparatorChar; 
      } 
     } 

     HashSet<string> found = new HashSet<string>(); 

     foreach (ZipArchiveEntry entry in archive.Entries) 
     { 
      if (path.Length > 0 && !entry.FullName.StartsWith(path)) 
      { 
       continue; 
      } 

      int nextSeparator = entry.FullName.IndexOfAny(_pathSeparators, path.Length); 

      if (nextSeparator >= 0) 
      { 
       string directoryName = entry.FullName.Substring(0, nextSeparator + 1); 

       if (found.Add(directoryName)) 
       { 
        if (entryInfoTypes.HasFlag(EntryInfoTypes.Directory)) 
        { 
         yield return new ZipDirectoryInfo(directoryName); 
        } 

        if (searchOption == SearchOption.AllDirectories) 
        { 
         foreach (ZipEntryInfo info in 
          archive.EnumerateEntryInfos(
           directoryName, searchOption, entryInfoTypes)) 
         { 
          yield return info; 
         } 
        } 
       } 
      } 
      else 
      { 
       if (entryInfoTypes.HasFlag(EntryInfoTypes.File)) 
       { 
        yield return new ZipFileInfo(entry.FullName); 
       } 
      } 
     } 
    } 
} 

public class ZipEntryInfo 
{ 
    public string Name { get; } 

    public ZipEntryInfo(string name) 
    { 
     Name = name; 
    } 
} 
public class ZipDirectoryInfo : ZipEntryInfo 
{ 
    public ZipDirectoryInfo(string name) : base(name) { } 
} 

public class ZipFileInfo : ZipEntryInfo 
{ 
    public ZipFileInfo(string name) : base(name) { } 
} 

注:

  • 这一点很重要要记住,即使有这个实现,你也不是真的处理实际的目录。这只是浏览档案的便利。尤其是,对于归档条目,您永远无法获得类似.NET的实际DirectoryInfo类的任何内容,因为可能看起来像单个目录的内容实际上只是一个或多个归档条目路径的一部分。诸如Attributes,CreationTime和类似的属性没有任何意义。如果你想,你可以可能虽然包括像NameParent()(当然,我的Name属性重命名为FullName,所以它更好地匹配DirectoryInfo类)。
  • 我将它实现为扩展方法,因为我发现调用代码更具可读性。
  • 有助手类型分别表示文件和目录条目。当然,你可以传回字符串,但是这会让实现可以返回两种条目的基本方法困难得多。
  • 我包含了不同方法的重载,为SearchOption参数提供默认值。我想我可以在方法声明中设置默认值;旧习难改。 :)
  • 以上是关于简单的实现,我可以拿出来,但在一个小的成本效率。递归的每个级别枚举归档条目的整个集合,因为它搜索当前路径中的条目。通过维护存在的所有条目的HashSet<string>,可以显着提高效率,当条目匹配并且被有效使用时删除条目。这会使代码显着复杂化,所以我把它作为读者的练习。

您可以使用库如SevenZipSharp来查看zip文件。该库是为7z格式设计的,但它也可以访问各种其他存档格式。