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

时间:2016-09-24 17:06:04

标签: c#

我的路径是C:\ Users \ xx \ Desktop \ Folder \ E_sa_sub.zip \ E_sa_sub \ subbb
问题是E_sa_sub.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);
        }
    }

2 个答案:

答案 0 :(得分:2)

.zip存档文件实际上不是Windows文件系统目录,因此您尝试做的事情根本不起作用。另外,.zip存档文件甚至没有真正的目录。它具有名称的归档条目,其中这些名称可以包含路径分隔符(与Windows文件系统上的路径不同,如NTFS或FAT)。

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

对于Windows资源管理器(即Windows的GUI shell),当您双击.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中获取类似于.NET的实际DirectoryInfo类的内容,因为可能看起来像单个目录只是一个或多个路径的一部分档案条目。像AttributesCreationTime这样的属性和类似的东西都没有任何意义。如果您愿意,可以但是包含NameParent之类的内容(当然,将Name属性重命名为FullName,以便更好地匹配DirectoryInfo类)。
  • 我将它实现为扩展方法,因为我发现调用代码更具可读性。
  • 有辅助类型分别表示文件和目录条目。当然,您可以只返回字符串,但这会使实现可以返回两种条目的基本方法变得更加困难。
  • 我包含了不同方法的重载,以便为SearchOption参数提供默认值。我想我只能在方法声明中设置默认值;旧习难改。 :)
  • 以上是我能想到的简单实现,但效率很低。每个递归级别在搜索当前路径中的条目时枚举整个归档条目集合。通过维护所有条目的HashSet<string>,在匹配并有效消耗条目时删除条目,可以显着提高效率。这会使代码变得非常复杂,所以我把它作为读者的练习。

答案 1 :(得分:0)

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