CSharp methods for compressing and decompressing files asynchronously. Taken from C# 6.0 Cookbook by Jay Hilyard and Stephen Teilhet.
public static async void TestCompressNewFileAsync()
{
byte[] data = new byte[10000000];
for (int i = 0; i < 10000000; i++)
data[i] = (byte)i;
string normalFilePath = Path.GetTempPath() + "NewNormalFile.txt";
string deflateCompressedFilePath = Path.GetTempPath() + "NewCompressedFile.txt";
string deflateDecompressedFilePath = Path.GetTempPath() + "NewDecompressedFile.txt";
string gzCompressedFilePath = Path.GetTempPath() + "NewGZCompressedFile.txt";
string gzDecompressedFilePath = Path.GetTempPath() + "NewGZDecompressedFile.txt";
Console.WriteLine($"Base file: {normalFilePath}");
Console.WriteLine($"Deflate compressed file: {deflateCompressedFilePath}");
Console.WriteLine($"Defalte decompressed file: {deflateDecompressedFilePath}");
Console.WriteLine($"GZip compressed file: {gzCompressedFilePath}");
Console.WriteLine($"GZip decompressed file: {gzDecompressedFilePath}");
using (FileStream fs =
new FileStream(normalFilePath,
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None,
4096, useAsync:true))
{
await fs.WriteAsync(data, 0, data.Length);
}
await CompressFileAsync(normalFilePath, deflateCompressedFilePath,
CompressionType.Deflate);
await DecompressFileAsync(deflateCompressedFilePath, deflateDecompressedFilePath,
CompressionType.Deflate);
await CompressFileAsync(normalFilePath, gzCompressedFilePath,
CompressionType.GZip);
await DecompressFileAsync(gzCompressedFilePath, gzDecompressedFilePath,
CompressionType.GZip);
//Normal file size == 10,000,000 bytes
//GZipped file size == 84,362
//Deflated file size == 42,145
//Pre .NET 4.5 GZipped file size == 155,204
//Pre .NET 4.5 Deflated file size == 155,168
// 36 bytes are related to the GZip CRC
}
/// <summary>
/// Compress the source file to the destination file.
/// This is done in 1MB chunks to not overwhelm the memory usage.
/// </summary>
/// <param name="sourceFile">the uncompressed file</param>
/// <param name="destinationFile">the compressed file</param>
/// <param name="compressionType">the type of compression to use</param>
public static async Task CompressFileAsync(string sourceFile,
string destinationFile,
CompressionType compressionType)
{
if (string.IsNullOrWhiteSpace(sourceFile))
throw new ArgumentNullException(nameof(sourceFile));
if (string.IsNullOrWhiteSpace(destinationFile))
throw new ArgumentNullException(nameof(destinationFile));
FileStream streamSource = null;
FileStream streamDestination = null;
Stream streamCompressed = null;
int bufferSize = 4096;
using (streamSource = new FileStream(sourceFile,
FileMode.OpenOrCreate, FileAccess.Read, FileShare.None,
bufferSize, useAsync: true))
{
using (streamDestination = new FileStream(destinationFile,
FileMode.OpenOrCreate, FileAccess.Write, FileShare.None,
bufferSize, useAsync: true))
{
// read 1MB chunks and compress them
long fileLength = streamSource.Length;
// write out the fileLength size
byte[] size = BitConverter.GetBytes(fileLength);
await streamDestination.WriteAsync(size, 0, size.Length);
long chunkSize = 1048576; // 1MB
while (fileLength > 0)
{
// read the chunk
byte[] data = new byte[chunkSize];
await streamSource.ReadAsync(data, 0, data.Length);
// compress the chunk
MemoryStream compressedDataStream =
new MemoryStream();
if (compressionType == CompressionType.Deflate)
streamCompressed =
new DeflateStream(compressedDataStream,
CompressionMode.Compress);
else
streamCompressed =
new GZipStream(compressedDataStream,
CompressionMode.Compress);
using (streamCompressed)
{
// write the chunk in the compressed stream
await streamCompressed.WriteAsync(data, 0, data.Length);
await streamCompressed.FlushAsync();
}
// get the bytes for the compressed chunk
byte[] compressedData =
compressedDataStream.GetBuffer();
// write out the chunk size
size = BitConverter.GetBytes(chunkSize);
await streamDestination.WriteAsync(size, 0, size.Length);
// write out the compressed size
size = BitConverter.GetBytes(compressedData.Length);
await streamDestination.WriteAsync(size, 0, size.Length);
// write out the compressed chunk
await streamDestination.WriteAsync(compressedData, 0,
compressedData.Length);
await streamDestination.FlushAsync();
// subtract the chunk size from the file size
fileLength -= chunkSize;
// if chunk is less than remaining file use
// remaining file
if (fileLength < chunkSize)
chunkSize = fileLength;
}
}
}
}
/// <summary>
/// This function will decompress the chunked compressed file
/// created by the CompressFile function.
/// </summary>
/// <param name="sourceFile">the compressed file</param>
/// <param name="destinationFile">the destination file</param>
/// <param name="compressionType">the type of compression to use</param>
public static async Task DecompressFileAsync(string sourceFile,
string destinationFile,
CompressionType compressionType)
{
if (string.IsNullOrWhiteSpace(sourceFile))
throw new ArgumentNullException(nameof(sourceFile));
if (string.IsNullOrWhiteSpace(destinationFile))
throw new ArgumentNullException(nameof(destinationFile));
FileStream streamSource = null;
FileStream streamDestination = null;
Stream streamUncompressed = null;
int bufferSize = 4096;
using (streamSource = new FileStream(sourceFile,
FileMode.OpenOrCreate, FileAccess.Read, FileShare.None,
bufferSize, useAsync: true))
{
using (streamDestination = new FileStream(destinationFile,
FileMode.OpenOrCreate, FileAccess.Write, FileShare.None,
bufferSize, useAsync: true))
{
// read the fileLength size
// read the chunk size
byte[] size = new byte[sizeof(long)];
await streamSource.ReadAsync(size, 0, size.Length);
// convert the size back to a number
long fileLength = BitConverter.ToInt64(size, 0);
long chunkSize = 0;
int storedSize = 0;
long workingSet = Process.GetCurrentProcess().WorkingSet64;
while (fileLength > 0)
{
// read the chunk size
size = new byte[sizeof(long)];
await streamSource.ReadAsync(size, 0, size.Length);
// convert the size back to a number
chunkSize = BitConverter.ToInt64(size, 0);
if (chunkSize > fileLength ||
chunkSize > workingSet)
throw new InvalidDataException();
// read the compressed size
size = new byte[sizeof(int)];
await streamSource.ReadAsync(size, 0, size.Length);
// convert the size back to a number
storedSize = BitConverter.ToInt32(size, 0);
if (storedSize > fileLength ||
storedSize > workingSet)
throw new InvalidDataException();
if (storedSize > chunkSize)
throw new InvalidDataException();
byte[] uncompressedData = new byte[chunkSize];
byte[] compressedData = new byte[storedSize];
await streamSource.ReadAsync(compressedData, 0,
compressedData.Length);
// uncompress the chunk
MemoryStream uncompressedDataStream =
new MemoryStream(compressedData);
if (compressionType == CompressionType.Deflate)
streamUncompressed =
new DeflateStream(uncompressedDataStream,
CompressionMode.Decompress);
else
streamUncompressed =
new GZipStream(uncompressedDataStream,
CompressionMode.Decompress);
using (streamUncompressed)
{
// read the chunk in the compressed stream
await streamUncompressed.ReadAsync(uncompressedData, 0,
uncompressedData.Length);
}
// write out the uncompressed chunk
await streamDestination.WriteAsync(uncompressedData, 0,
uncompressedData.Length);
// subtract the chunk size from the file size
fileLength -= chunkSize;
// if chunk is less than remaining file use remaining file
if (fileLength < chunkSize)
chunkSize = fileLength;
}
}
}
}
public enum CompressionType
{
Deflate,
GZip
}