Stavros
Stavros

Reputation: 6140

How to create tar.gz file in C#


I have tried SevenZipLib and SevenZipSharp, but without success. Can someone give me a working example of archiving a text file to tar.gz, using any free library?
I know it's not the best way to go for zipping, but it's a requirement I have.

Upvotes: 13

Views: 22866

Answers (3)

Daniel Fisher  lennybacon
Daniel Fisher lennybacon

Reputation: 4194

Since .net7 you can do it like this:

using System.Formats.Tar;
using System.IO.Compression;

string sourceDir = "./my-files";
string outputFile = "./myarchive.tar.gz";

using FileStream fs = new(outputFile, FileMode.CreateNew, FileAccess.Write);
using GZipStream gz = new(fs, CompressionMode.Compress, leaveOpen: true);

TarFile.CreateFromDirectory(sourceDir, gz, includeBaseDirectory: false);

Source

Upvotes: 0

Window
Window

Reputation: 1437

Not a fan of the examples on how to compress a directory provided by SharpZipLib because instead of keeping the folder structure in the directory you are trying to convert to a .tgz, their method copies the folder path that leads to whatever directory you are trying to compress.

For example:

If you were trying to compress folderA (located in: //filer/folder1/folderA) with a single file called foo.c, then after converting folderA into a .tgz, the path to foo.c within the .tgz file would be:

folderA.tgz/filer/folder1/foo.c

This solution omits the extra files within the .tgz, resulting in:

folderA.tgz/foo.c

using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;

/// <summary>
/// Creates a .tgz file using everything in the sourceDirectory. The .tgz file will be titled {tgzFileName}.tgz and will be located in targetDirectory.
/// </summary>
/// <param name="sourceDirectory">Directory to compress into a .tgz</param>
/// <param name="tgzFileName">Name of .tgz file</param>
/// <param name="targetDirectory">Directory where {tgzFileName}.tgz should be located.</param>
/// <param name="deleteSourceDirectoryUponCompletion">Will delete sourceDirectory if <see langword="true"/></param>
/// <returns>Path to .tgz file</returns>
public string CreateTGZ(string sourceDirectory, string tgzFileName, string targetDirectory, bool deleteSourceDirectoryUponCompletion = false)
{
    if (!tgzFileName.EndsWith(".tgz"))
    {
        tgzFileName = tgzFileName + ".tgz";
    }
    using (var outStream = File.Create(Path.Combine(targetDirectory, tgzFileName)))
    using (var gzoStream = new GZipOutputStream(outStream))
    {
        var tarArchive = TarArchive.CreateOutputTarArchive(gzoStream);

        // Note that the RootPath is currently case sensitive and must be forward slashes e.g. "c:/temp"
        // and must not end with a slash, otherwise cuts off first char of filename
        tarArchive.RootPath = sourceDirectory.Replace('\\', '/');
        if (tarArchive.RootPath.EndsWith("/"))
        {
            tarArchive.RootPath = tarArchive.RootPath.Remove(tarArchive.RootPath.Length - 1);
        }

        AddDirectoryFilesToTGZ(tarArchive, sourceDirectory);

        if (deleteSourceDirectoryUponCompletion)
        {
            File.Delete(sourceDirectory);
        }

        var tgzPath = (tarArchive.RootPath + ".tgz").Replace('/', '\\');

        tarArchive.Close();
        return tgzPath;
    }
}

private void AddDirectoryFilesToTGZ(TarArchive tarArchive, string sourceDirectory)
{
    AddDirectoryFilesToTGZ(tarArchive, sourceDirectory, string.Empty);
}

private void AddDirectoryFilesToTGZ(TarArchive tarArchive, string sourceDirectory, string currentDirectory)
{
    var pathToCurrentDirectory = Path.Combine(sourceDirectory, currentDirectory);

    // Write each file to the tgz.
    var filePaths = Directory.GetFiles(pathToCurrentDirectory);
    foreach (string filePath in filePaths)
    {
        var tarEntry = TarEntry.CreateEntryFromFile(filePath);

        // Name sets where the file is written. Write it in the same spot it exists in the source directory
        tarEntry.Name = filePath.Replace(sourceDirectory, "");

        // If the Name starts with '\' then an extra folder (with a blank name) will be created, we don't want that.
        if (tarEntry.Name.StartsWith('\\'))
        {
            tarEntry.Name = tarEntry.Name.Substring(1);
        }
        tarArchive.WriteEntry(tarEntry, true);
    }

    // Write directories to tgz
    var directories = Directory.GetDirectories(pathToCurrentDirectory);
    foreach (string directory in directories)
    {
        AddDirectoryFilesToTGZ(tarArchive, sourceDirectory, directory);
    }
}

Upvotes: 4

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131704

Perhaps the most popular package in NuGet that supports TAR is SharpZipLib. Its wiki includes examples for working with tar.gz files, including creation. The linked example archives an entire folder.

To archive a single file, the sample can be simplified to this:

private void CreateTarGZ(string tgzFilename, string fileName)
{
    using (var outStream = File.Create(tgzFilename))
    using (var gzoStream = new GZipOutputStream(outStream))
    using (var tarArchive = TarArchive.CreateOutputTarArchive(gzoStream))
    {
        tarArchive.RootPath = Path.GetDirectoryName(fileName);

        var tarEntry = TarEntry.CreateEntryFromFile(fileName);
        tarEntry.Name = Path.GetFileName(fileName);

        tarArchive.WriteEntry(tarEntry,true);
    }
}

Essentially, you need to create a TarEntry for each folder and file you want to store.

Upvotes: 16

Related Questions