.net 使用C#解压缩tar文件

xkftehaa  于 2023-06-07  发布在  .NET
关注(0)|答案(6)|浏览(715)

我正在寻找一种方法来添加嵌入式资源到我的解决方案。这些资源将是包含大量文件的文件夹。根据用户需求,需要对它们进行解压缩。
我正在寻找一种方法做存储在可执行文件夹中,而不涉及第三方库(看起来相当愚蠢,但这是任务)。
我发现,我可以GZip和UnGZip他们使用标准库.但是GZip只处理单个文件。在这种情况下,TAR应该到现场。但是我还没有在标准类中找到TAR实现。
也许可以用纯C#解压缩TAR?

w3nuxt5m

w3nuxt5m1#

在寻找同一个问题的快速答案时,我遇到了这个线程,并且对当前的答案并不完全满意,因为它们都指向使用第三方依赖项到更大的库,所有这些都只是为了实现将tar.gz文件简单提取到磁盘。
虽然gz可能被认为相当复杂,但tar却相当简单。在其核心,它只需要一堆文件,为每个描述文件的文件添加一个500字节的头(但需要512字节),并将它们全部写入512字节对齐的单个存档。没有压缩,通常通过将创建的文件压缩到gz存档来处理,.NET内置了这个存档,它可以处理所有困难的部分。
在查看了tarspec之后,我们只需要从头文件中挑选出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);
    }
}

这里有一些帮助函数,用于从文件打开,并在提取之前自动首先解压缩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);
}

下面是完整文件的gist和一些注解。

s6fujrry

s6fujrry2#

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)
            {
                ExtractionOptions opt = new ExtractionOptions {
                    ExtractFullPath = true,
                    Overwrite = true
                };
                reader.WriteEntryToDirectory(directoryPath, opt);
            }
        }
    }
}
vwkv1x7d

vwkv1x7d3#

参见tar-cs

using (FileStream unarchFile = File.OpenRead(tarfile))
{
    TarReader reader = new TarReader(unarchFile);
    reader.ReadToEnd("out_dir");
}
mklgxw1f

mklgxw1f4#

由于不允许使用外部库,因此您也不限于tar文件的特定格式。事实上,他们甚至不需要将所有内容都放在同一个文件中。
您可以用C#编写自己的tar类实用程序,它遍历目录树并生成两个文件:一个“头”文件,由一个序列化的字典组成,将System.IO.Path示例Map到偏移量/长度对,以及一个大文件,包含连接成一个巨大blob的各个文件的内容。这不是一个微不足道的任务,但也不是过于复杂。

vuktfyat

vuktfyat5#

在. NET中有两种压缩/解压缩的方法,首先你可以使用Gzipstream类和DeflatStream都可以将你的文件压缩成. gz格式,所以如果你在Gzipstream中压缩了任何文件,它可以用任何流行的压缩应用程序打开,如winzip/winrar,7zip,但你不能用DeflatStream打开压缩文件。这两个类来自. NET 2。
还有另一种方法是Package类,它实际上与Gzipstream和DeflatStream相同,唯一不同的是你可以压缩多个文件,然后可以用winzip/winrar打开,www.example.com就是. NET的全部。7zip.so如果你解压缩任何带有包类docx文件,你可以看到里面存储的所有东西。所以不要使用. NET库进行压缩或解压缩,因为您甚至不能生成通用压缩文件或解压缩通用zip文件。您必须考虑使用第三方库,例如http://www.icsharpcode.net/OpenSource/SharpZipLib/ so don't use .NET libraries for compressing or even decompressing cause you can't even make a generic compress file or even decompress a generic zip file. you have to consider for a third party library such as http://www.icsharpcode.net/OpenSource/SharpZipLib/
或者从底层开始执行所有的事情。

cyvaqqii

cyvaqqii6#

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

提取到目录:

await TarFile.ExtractToDirectoryAsync(tarFilePath, outputDir);

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

相关问题