Как упаковать большой файл в Tar?
Если вы используете этот код, то используется много оперативной памяти.
byte[] buffer = ArrayPool<byte>.Shared.Rent(1024 * 1024);
string item = @"R:\Bigfile.dat";
using (FileStream _writestream = File.Create(@"R:\test.tar")
using (TarWriter tarwriter = new(_writestream, TarEntryFormat.Pax, leaveOpen: false))
using (FileStream srcFile = new(item, FileMode.Open, FileAccess.Read))
{
string fileName = item.Remove(0, mainDir.Length + 1).Replace('\\', '/');
PaxTarEntry te = new(TarEntryType.RegularFile, fileName)
{
DataStream = new MemoryStream()
};
int currentBlockSize = 0;
while ((currentBlockSize = srcFile.Read(buffer)) > 0)
{
te.DataStream.Write(buffer.AsSpan(0, currentBlockSize));
}
te.DataStream.Position = 0;
tarwriter.WriteEntry(te);
}
ArrayPool<byte>.Shared.Return(buffer);
Если вы используете этот код, он будет использовать много памяти. Да, возможно, потому что это будет записано в MemoryStream(). Но какие варианты лучше, если нужно написать ОЧЕНЬ большой файл? Я хочу записывать в файл .tar вместо ОЗУ и хочу контролировать размер каждой итерации цикла записи с помощью размера буфера.
@JonSkeet Я исправил это. Но я не знаю, куда писать, как не в MemoryStream?
Можете ли вы просто не использовать srcFile вместо DataStream? (Это то, что я ожидал сделать — я бы просто использовал File.OpenRead вместо явного вызова конструктора FileStream, просто для простоты...)
Непонятно, что имеется в виду под «Я хочу контролировать размер каждой итерации цикла записи через размер буфера» - итерацию чего? (В моем предложенном решении вам нечего будет повторять...)





using System.IO;
using System.IO.Compression;
public void PackLargeFileIntoTar(string sourceFilePath, string tarFilePath)
{
using (FileStream tarFileStream = new FileStream(tarFilePath, FileMode.Create, FileAccess.Write))
using (TarArchive tarArchive = TarArchive.Create())
{
tarArchive.WriteTo(tarFileStream, TarEntryFormat.Pax, leaveOpen: false);
using (FileStream srcFile = new FileStream(sourceFilePath, FileMode.Open, FileAccess.Read))
{
string fileName = Path.GetFileName(sourceFilePath);
var entry = TarEntry.CreateEntryFromFile(sourceFilePath);
tarArchive.AddEntry(fileName, srcFile);
}
}
}
Откуда взялся TarArchive? Насколько я знаю, встроенная реализация Tar — TarFile.
Я обновил вопрос. Кажется, ваш ответ не подходит. Он не использует буфер, и вы также используете реализацию .net поддержки .tar, которая не встроена в .net.
@Raf-9600: «и вы также используете .net-реализацию поддержки .tar, которая не встроена в .net» - в вашем вопросе это не оговаривалось как требование. Я согласен, что ответ мог бы быть более ясным, но если вы собираетесь отклонить любой ответ, в котором используется сторонняя библиотека, это обязательно должно быть в вопросе.
@Raf-9600: Также неясно, что вы подразумеваете под «он не использует буфер» - какое именно поведение вам требуется, связанное с буферизацией? (Что вы имеете в виду в вопросе «Я хочу контролировать размер каждой итерации цикла записи через размер буфера»?
@JonSkeet Я хотел бы иметь возможность отображать ход работы с пакетом .tar, обычно для этого нужно использовать буфер. Но я не знаю, как поступить в данном случае.
@Raf-9600: Raf-9600: Опять же, в вопросе об этом не упоминается. Потенциально вы могли бы создать оболочку потока, которая подсчитывает прочитанные байты и т. д., но это было бы относительно неудобно. Я бы взвесил, насколько сильно вы этого хотите, по сравнению с тем, насколько это будет хлопотно.
Я думаю вам просто вообще не нужно делать копирование из входного потока в память. Вот короткая, но полная программа, которая создает tar-файл из каждого аргумента командной строки:
using System.Formats.Tar;
using var output = File.Create("output.tar");
using var tarWriter = new TarWriter(output, TarEntryFormat.Pax);
foreach (var arg in args)
{
using var input = File.OpenRead(arg);
var entry = new PaxTarEntry(TarEntryType.RegularFile, arg)
{
DataStream = input
};
tarWriter.WriteEntry(entry);
}
Я только что использовал этот код для создания tar-файла размером 20 ГБ (из нескольких файлов размером 1–3 ГБ), а диспетчер задач показал его только с использованием 3,6 МБ...
@JonSkeet Почему бы не использовать асинхронные версии? await using var outputawait using var tarWriterawait using var input = File.OpenReadawait tarWriter.WriteEntryAsync
@Charlieface: Да, ты вполне можешь это сделать. Но учитывая, что исходный код не является асинхронным и нет никаких указаний на необходимость сделать его асинхронным, казалось, что было бы неправильно делать это в одностороннем порядке.
Что здесь
_writestream? Если бы вы могли предоставить минимально воспроизводимый пример , вам было бы легче помочь. (И вы явно создаете MemoryStream, в который пишете, поэтому неудивительно, что он занимает много памяти, IMO...)