Распакуйте tar-файлы, используя C#
Я ищу способ добавить встроенный ресурс в мое решение. Этими ресурсами будут папки с большим количеством файлов. По требованию пользователя они должны быть распакованы.
Я ищу способ хранения таких папок в исполняемом файле без привлечения сторонних библиотек (выглядит довольно глупо, но это задача).
Я обнаружил, что могу использовать GZip и UnGZip, используя стандартные библиотеки. Но GZip обрабатывает только один файл. В таких случаях TAR должен прийти на место происшествия. Но я не нашел реализацию TAR среди стандартных классов.
Может быть, возможно распаковать TAR с голым C#?
6 ответов
Поскольку вам не разрешено использовать внешние библиотеки, вы не ограничены определенным форматом tar
файл тоже. На самом деле, им даже не нужно, чтобы все было в одном файле.
Вы можете написать свою собственную tar-подобную утилиту на C#, которая обходит дерево каталогов и создает два файла: файл заголовка, состоящий из сериализованного отображения словаря System.IO.Path
экземпляры для пар смещение / длина и большой файл, содержащий содержимое отдельных файлов, объединенных в один гигантский большой двоичный объект. Это не тривиальная задача, но и не слишком сложная.
Ища быстрый ответ на тот же вопрос, я наткнулся на эту ветку и не был полностью удовлетворен текущими ответами, поскольку все они указывают на использование сторонних зависимостей для гораздо более крупных библиотек, все просто для достижения простого извлечения 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);
}
}
А вот несколько вспомогательных функций для открытия из файла и автоматизации первой распаковки 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);
}
Вот суть полного файла с некоторыми комментариями.
Тар-цы сделают эту работу, но она довольно медленная. Я бы порекомендовал использовать 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);
}
}
}
}
В .NET 7 добавлено несколько классов для работы с файлами TAR:
- System.Formats.Tar.TarFile для упаковки каталога в файл TAR или извлечения файла TAR в каталог;
- System.Formats.Tar.TarReader для чтения файла TAR; и
- System.Formats.Tar.TarWriter для записи файла 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));
}
Смотрите tar-cs
using (FileStream unarchFile = File.OpenRead(tarfile))
{
TarReader reader = new TarReader(unarchFile);
reader.ReadToEnd("out_dir");
}
Существует два способа сжатия / распаковки в.NET. Во-первых, вы можете использовать класс Gzipstream и DeflatStream, которые на самом деле могут сжимать ваши файлы в формате.gz, поэтому, если вы сжимаете любой файл в Gzipstream, его можно открыть с помощью любых популярных приложений сжатия, таких как winzip/ winrar, 7zip, но вы не можете открыть сжатый файл с помощью DeflatStream. эти два класса из.NET 2.
и есть еще один способ, который представляет собой класс Package, он фактически такой же, как Gzipstream и DeflatStream, единственное отличие состоит в том, что вы можете сжать несколько файлов, которые затем можно открыть с помощью winzip/ winrar, 7zip.so, и это все, что есть в.NET. но это даже не общий файл.zip, это то, что Microsoft использует для сжатия своих офисных файлов с расширением *x. если вы распакуете любой файл docx с помощью класса пакета, вы сможете увидеть все, что в нем хранится. так что не используйте библиотеки.NET для сжатия или даже распаковки, потому что вы даже не можете сделать общий файл сжатия или даже распаковать общий файл zip. Вы должны рассмотреть для сторонней библиотеки, такой как http://www.icsharpcode.net/OpenSource/SharpZipLib/
или реализовать все с первого этажа.