TAR bundles files and metadata without compression.
dotnet add package RecursiveAI.OneArchiveRequires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.
using RecursiveAI.OneArchive; File.WriteAllText("hello.txt", "Hello!"); Archive.Create("hello.tar") .Add("hello.txt").Save(); Console.WriteLine("Wrote hello.tar");
Wrote hello.tarUnzip the example, then dotnet run.
The full program
The program generates its own input text: hello.txt contains Hello! and notes.txt contains Keep this file., each followed by a newline.
using RecursiveAI.OneArchive; File.WriteAllText("hello.txt", "Hello!\n"); File.WriteAllText("notes.txt", "Keep this file.\n"); Archive.Create("bundle.tar") .Add("hello.txt", "docs/hello.txt") .Add("notes.txt", "docs/notes.txt").Save(); Console.WriteLine("Wrote bundle.tar"); using var archive = Archive.Open("bundle.tar"); foreach (var entry in archive.Entries) { Console.WriteLine($"{entry.Name}: {entry.Size} bytes"); using var content = archive.OpenEntry(entry); using var text = new StreamReader(content); Console.Write(text.ReadToEnd()); }
dotnet run prints:
Wrote bundle.tar docs/hello.txt: 7 bytes Hello! docs/notes.txt: 16 bytes Keep this file.
Missing source paths raise FileNotFoundException; a source whose size changes before Save finishes is rejected.
Variations
Write ustar with explicit metadata
This standalone variation supplies bytes and metadata instead of reading a source file. Decimal 420 is the Unix permission value 0644.
using RecursiveAI.OneArchive; using RecursiveAI.OneArchive.Tar; var options = new ArchiveCreateOptions { Tar = new TarWriterOptions { Format = TarFormat.Ustar } }; byte[] content = "Hello!"u8.ToArray(); var entry = new ArchiveEntry { Name = "docs/hello.txt", Kind = ArchiveEntryKind.File, Size = content.Length, ModifiedTime = DateTimeOffset.UnixEpoch, Mode = (UnixFileMode)420 // 0644 in octal }; Archive.Create("portable.tar", options).Add(entry, content).Save(); Console.WriteLine("Wrote portable.tar");
Wrote portable.tarRelated recipes
The short answer
Create a plain TAR archive with Archive.Create("bundle.tar"), chain Add calls for the files, and finish with Save. OneArchive chooses TAR from the extension and writes pax format by default. No compression layer is added for .tar.
The full program generates two text inputs, stores them under docs/, and opens the resulting archive to list and read both entries. Replace the generated paths with your own files when integrating it into an application.
How do I create a TAR archive from multiple files in C#?
Add each file to the same builder before calling Save. The second argument to Add is the path inside the TAR, not another source path. This lets you collect files from different disk locations into one logical directory without first copying them to a staging folder.
To include a directory, pass its path to Add. The builder adds a directory entry followed by its contents, sorting each directory’s children by ordinal name. An empty second argument puts a directory’s contents at the archive root instead of preserving its base name. Archive paths use / separators.
The builder reads content during Save. A file whose size changes after it was added causes an error, so generate or finish writing inputs before collecting them. Adding files does not write the archive by itself. Save once after the complete list is ready, and keep your output separate from the input tree.
How do I use a TAR writer with entry metadata in C#?
Use an ArchiveEntry and a byte array when you need to specify metadata yourself. The variation above sets the archive path, file kind, byte length, modification time and Unix mode. Its content is the UTF-8 bytes of Hello!; Size must equal that array’s length. A mismatch raises ArgumentException rather than silently padding the content.
File-system inputs obtain modification times and permission bits from the file system. The builder does not read user and group IDs from managed code: those IDs default to zero, without owner names. Supply entry objects when you need chosen ownership or timestamps. This is also useful for archives assembled from generated text rather than existing disk files.
Pax is TAR with extended metadata records, while ustar is the older fixed-header TAR format; choose ustar when a receiving tool requires it.
The default writer uses pax; the variation selects ustar through TarWriterOptions.Format. The TAR documentation documents both output formats. The reader supports additional historical TAR forms, but that does not mean the writer emits every form it can read. Multi-volume archives are not supported.
How do I read a TAR archive in C#?
Open the completed file with Archive.Open, then iterate its Entries. The full program opens each entry’s content stream with OpenEntry and reads the generated text through StreamReader. Dispose each content stream before moving on, and dispose the archive when the listing is done.
The byte count is the entry’s uncompressed size, not the size of the whole TAR on disk. Header records and padding also occupy space. Use a text reader only for text payloads; binary files should stay as streams or bytes. Opening an arbitrary TAR does not make every entry a text file.