使用C#解压缩tar文件

shy*_*kov 9 .net c# tar

我正在寻找一种方法来为我的解决方案添加嵌入式资源.此资源将是包含大量文件的文件夹.根据用户需求,他们需要解压缩.

我正在寻找一种方法来存储这样的文件夹在可执行文件中而不涉及第三方库(看起来相当愚蠢,但这是任务).

我发现,我可以使用标准库GZip和UnGZip.但是GZip只处理单个文件.在这种情况下,TAR应该来到现场.但我没有在标准类中找到TAR实现.

也许有可能用裸露的C#解压缩TAR?

cku*_*uri 8

.NET 7 添加了几个类来处理 TAR 文件:

解压到目录:

await TarFile.ExtractToDirectoryAsync(tarFilePath, outputDir);
Run Code Online (Sandbox Code Playgroud)

枚举 TAR 文件并手动提取其条目:

await using var tarStream = new FileStream(tarFilePath, new FileStreamOptions { Mode = FileMode.Open, Access = FileAccess.Read, Options = FileOptions.Asynchronous });
await using var tarReader = new TarReader(tarStream);
TarEntry entry;
while ((entry = await tarReader.GetNextEntryAsync()) != null)
{
  if (entry.EntryType is TarEntryType.SymbolicLink or TarEntryType.HardLink or TarEntryType.GlobalExtendedAttributes)
  {
     continue;
  }

  Console.WriteLine($"Extracting {entry.Name}");
  await entry.ExtractToFileAsync(Path.Join(outputDirectory, entry.Name));
}
Run Code Online (Sandbox Code Playgroud)


For*_*er0 7

在寻找同一问题的快速答案时,我遇到了这个问题,对当前的答案并不完全满意,因为它们都指向对更大的库使用第三方依赖,所有这些都只是为了简单地提取a tar.gz文件到磁盘。

尽管gz可以认为格式相当复杂,tar但另一方面却很简单。从本质上讲,它只需要处理一堆文件,为每个描述文件的文件添加一个500字节的标头(但需要512字节),然后将它们全部以512字节的对齐方式写入单个存档。没有压缩,通常通过将创建的文件压缩到gz存档中来处理。.NET方便地内置了该存档,该存档处理了所有困难的部分。

在查看了格式规范之后tar,实际上只有2个值(尤其是在Windows上),我们需要从标头中挑选出来,以便从流中提取文件。第一个是name,第二个是size。使用这两个值,我们只需要查找流中的适当位置并将字节复制到文件中即可。

我做了一个非常基本的,肮脏的方法,将tar档案文件提取到目录中,并添加了一些帮助函数,用于从流或文件名中打开文件,并gz首先使用内置函数将文件解压缩。

主要方法是这样的:

public static void ExtractTar(Stream stream, string outputDir)
{
    var buffer = new byte[100];
    while (true)
    {
        stream.Read(buffer, 0, 100);
        var name = Encoding.ASCII.GetString(buffer).Trim('\0');
        if (String.IsNullOrWhiteSpace(name))
            break;
        stream.Seek(24, SeekOrigin.Current);
        stream.Read(buffer, 0, 12);
        var size = Convert.ToInt64(Encoding.ASCII.GetString(buffer, 0, 12).Trim(), 8);

        stream.Seek(376L, SeekOrigin.Current);

        var output = Path.Combine(outputDir, name);
        if (!Directory.Exists(Path.GetDirectoryName(output)))
            Directory.CreateDirectory(Path.GetDirectoryName(output));
        using (var str = File.Open(output, FileMode.OpenOrCreate, FileAccess.Write))
        {
            var buf = new byte[size];
            stream.Read(buf, 0, buf.Length);
            str.Write(buf, 0, buf.Length);
        }

        var pos = stream.Position;

        var offset = 512 - (pos  % 512);
        if (offset == 512)
            offset = 0;

        stream.Seek(offset, SeekOrigin.Current);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里有一些帮助程序功能,用于从文件打开,并tar.gz在提取之前自动先解压缩文件/流。

public static void ExtractTarGz(string filename, string outputDir)
{
    using (var stream = File.OpenRead(filename))
        ExtractTarGz(stream, outputDir);
}

public static void ExtractTarGz(Stream stream, string outputDir)
{
    // A GZipStream is not seekable, so copy it first to a MemoryStream
    using (var gzip = new GZipStream(stream, CompressionMode.Decompress))
    {
        const int chunk = 4096;
        using (var memStr = new MemoryStream())
        {
            int read;
            var buffer = new byte[chunk];
            do
            {
                read = gzip.Read(buffer, 0, chunk);
                memStr.Write(buffer, 0, read);
            } while (read == chunk);

            memStr.Seek(0, SeekOrigin.Begin);
            ExtractTar(memStr, outputDir);
        }
    }
}

public static void ExtractTar(string filename, string outputDir)
{
    using (var stream = File.OpenRead(filename))
        ExtractTar(stream, outputDir);
}
Run Code Online (Sandbox Code Playgroud)

这是完整文件的要点,并附有一些评论。

  • 仅供参考。您的“ Path.Join”调用仅在.NET Core 2.1中有效。为了使其更通用,请使用“ Path.Combine”。 (2认同)

Ste*_*lfe 6

Tar-cs可以完成这项工作,但是速度很慢。我建议使用SharpCompress,它明显更快。它还支持其他压缩类型,并且最近已更新。

using System;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Reader;

private static String directoryPath = @"C:\Temp";

public static void unTAR(String tarFilePath)
{
    using (Stream stream = File.OpenRead(tarFilePath))
    {
        var reader = ReaderFactory.Open(stream);
        while (reader.MoveToNextEntry())
        {
            if (!reader.Entry.IsDirectory)
            {
                reader.WriteEntryToDirectory(directoryPath, ExtractOptions.ExtractFullPath | ExtractOptions.Overwrite);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢你的回答!通过 2020 年的更新,ExtractOptions 现在通过实例化完成。例如 `WriteEntryToDirectory` 构造函数中的 `new ExtractionOptions() { ExtractFullPath = true, Overwrite = true}`。请参阅[此链接](https://github.com/adamhathcock/sharpcompress/blob/master/USAGE.md#extract-all-files-from-a-rar-file-to-a-directory-using-rararchive) (2认同)

das*_*ght 2

由于不允许您使用外部库,因此您tar也不受文件的特定格式的限制。事实上,他们甚至不需要将它们全部放在同一个文件中。

您可以用 C# 编写自己的类似 tar 的实用程序,它遍历目录树,并生成两个文件:一个“头”文件,其中包含将实例映射System.IO.Path到偏移/长度对的序列化字典,以及一个包含以下内容的大文件:单个文件连接成一个巨大的 blob。这不是一项微不足道的任务,但也不是太复杂。