How to Create, Extract and Update Tar GZIP File in Windows
Recently, our team needs to regularly update Tar GZIP files that are built for Linux on Windows. GZIP is a file format for file compression and decompression. Unlike ZIP, GZIP is used to compress just one single file. Usually, we have to assemble files into a single tar archive, and then compress that archive with gzip (.tar.gz or .tgz). I spent some time looking for the workaround of packaging TAR GZIP files on Windows.
Linux Commands
Windows 10 now supports installing Linux distributions as subsystems. To create tar.gz packages, it is convenient to use the Linux commands in command line tools.
Examples
Create a .tar.gz archive:
tar -czvf dwt.tar.gz dwt/
Extract an archive to a directory
tar -xzvf dwt.tar.gz -C tmp/
7zip Commands
7zip is competent for compressing and decompressing .tar.gz files in Windows.
Create .tar.gz:
7z a -ttar -so dwt.tar dwt/ | 7z a -si dwt.tar.gz
Extract .tar.gz:
7z x dwt.tar.gz -so | 7z x -si -ttar
Update tar.gz:
7z x dwt.tar.gz && 7z u dwt.tar dwt && del dwt.tar.gz && 7z a dwt.tar.gz dwt.tar && del dwt.tar
Create, Extract and Update TAR GZIP File in C#
To programmatically operating .tar.gz files, we can use SharpZipLib. The tutorial GZip and Tar Samples is also very helpful.
Create, Extract and Update TAR GZIP File in C#
Create a .tar.gz file
private void CreateTarGZ(string tgzFilename, string sourceDirectory)
{
Stream outStream = File.Create(tgzFilename);
Stream gzoStream = new GZipOutputStream(outStream);
TarArchive 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
// This is scheduled for fix in next release
tarArchive.RootPath = sourceDirectory.Replace('\\\\', '/');
if (tarArchive.RootPath.EndsWith("/"))
tarArchive.RootPath = tarArchive.RootPath.Remove(tarArchive.RootPath.Length - 1);
AddDirectoryFilesToTar(tarArchive, sourceDirectory, true, true);
tarArchive.Close();
}
private void AddDirectoryFilesToTar(TarArchive tarArchive, string sourceDirectory, bool recurse, bool isRoot)
{
// Optionally, write an entry for the directory itself.
// Specify false for recursion here if we will add the directory's files individually.
//
TarEntry tarEntry;
if (!isRoot)
{
tarEntry = TarEntry.CreateEntryFromFile(sourceDirectory);
tarArchive.WriteEntry(tarEntry, false);
}
// Write each file to the tar.
//
string[] filenames = Directory.GetFiles(sourceDirectory);
foreach (string filename in filenames)
{
tarEntry = TarEntry.CreateEntryFromFile(filename);
Console.WriteLine(tarEntry.Name);
tarArchive.WriteEntry(tarEntry, true);
}
if (recurse)
{
string[] directories = Directory.GetDirectories(sourceDirectory);
foreach (string directory in directories)
AddDirectoryFilesToTar(tarArchive, directory, recurse, false);
}
}
Extract a .tar.gz file
public void ExtractTGZ(String gzArchiveName, String destFolder)
{
Stream inStream = File.OpenRead(gzArchiveName);
Stream gzipStream = new GZipInputStream(inStream);
TarArchive tarArchive = TarArchive.CreateInputTarArchive(gzipStream);
tarArchive.ExtractContents(destFolder);
tarArchive.Close();
gzipStream.Close();
inStream.Close();
}
Update a .tar.gz file
Updating a .tar.gz file is a little complicated because we have to decompress .tar.gz to .tar first, and then update the .tar and compress it to .tar.gz again.
Extract .tar.gz to .tar:
public string ExtractGZipFile(string gzipFileName, string targetDir)
{
// Use a 4K buffer. Any larger is a waste.
byte[] dataBuffer = new byte[4096];
using (System.IO.Stream fs = new FileStream(gzipFileName, FileMode.Open, FileAccess.Read))
{
using (GZipInputStream gzipStream = new GZipInputStream(fs))
{
// Change this to your needs
string fnOut = Path.Combine(targetDir, Path.GetFileNameWithoutExtension(gzipFileName));
using (FileStream fsOut = File.Create(fnOut))
{
StreamUtils.Copy(gzipStream, fsOut, dataBuffer);
}
return fnOut;
}
}
}
Update .tar:
public void UpdateTar(string tarFileName, string targetFile, bool asciiTranslate)
{
using (FileStream fsIn = new FileStream(tarFileName, FileMode.Open, FileAccess.Read))
{
string tmpTar = Path.Combine(Path.GetDirectoryName(tarFileName), "tmp.tar");
using (FileStream fsOut = new FileStream(tmpTar, FileMode.OpenOrCreate, FileAccess.Write))
{
TarOutputStream tarOutputStream = new TarOutputStream(fsOut);
TarInputStream tarIn = new TarInputStream(fsIn);
TarEntry tarEntry;
while ((tarEntry = tarIn.GetNextEntry()) != null)
{
if (tarEntry.IsDirectory)
{
continue;
}
// Converts the unix forward slashes in the filenames to windows backslashes
//
string name = tarEntry.Name.Replace('/', Path.DirectorySeparatorChar);
string sourceFileName = Path.GetFileName(targetFile);
string targetFileName = Path.GetFileName(tarEntry.Name);
if (sourceFileName.Equals(targetFileName))
{
using (Stream inputStream = File.OpenRead(targetFile))
{
long fileSize = inputStream.Length;
TarEntry entry = TarEntry.CreateTarEntry(tarEntry.Name);
// Must set size, otherwise TarOutputStream will fail when output exceeds.
entry.Size = fileSize;
// Add the entry to the tar stream, before writing the data.
tarOutputStream.PutNextEntry(entry);
// this is copied from TarArchive.WriteEntryCore
byte[] localBuffer = new byte[32 * 1024];
while (true)
{
int numRead = inputStream.Read(localBuffer, 0, localBuffer.Length);
if (numRead <= 0)
{
break;
}
tarOutputStream.Write(localBuffer, 0, numRead);
}
}
tarOutputStream.CloseEntry();
}
else
{
tarOutputStream.PutNextEntry(tarEntry);
if (asciiTranslate)
{
CopyWithAsciiTranslate(tarIn, tarOutputStream);
}
else
{
tarIn.CopyEntryContents(tarOutputStream);
}
tarOutputStream.CloseEntry();
}
}
tarIn.Close();
tarOutputStream.Close();
}
File.Delete(tarFileName);
File.Move(tmpTar, tarFileName);
}
}
Update .tar.gz:
private void UpdateTarGZ(string tgzFilename, string tarFileName)
{
Stream gzoStream = new GZipOutputStream(File.Create(tgzFilename));
using (FileStream source = File.Open(tarFileName,
FileMode.Open))
{
byte[] localBuffer = new byte[32 * 1024];
while (true)
{
int numRead = source.Read(localBuffer, 0, localBuffer.Length);
if (numRead <= 0)
{
break;
}
gzoStream.Write(localBuffer, 0, numRead);
}
}
gzoStream.Close();
File.Delete(tarFileName);
}
Build the app:
dotnet publish -c Release -r win10-x64
Run the app using dotne run' or
targzip’: