Reputation: 8461
I am trying to understand why my code doesn't execute as desired. It creates a GZipStream, and then saves the object as compressed file on my hard drive, but the saved file is always 0 bytes.
Now I know how to save a file using GZipStream, but, my question is not how to do it. My question is purely why does this code save 0 bytes (or why FileStream works and memory doesn't).
private void BegingCompression()
{
var bytes = File.ReadAllBytes(this.fileName);
using (MemoryStream ms = new MemoryStream(bytes))
{
ms.ReadByte();
using (FileStream fs =new FileStream(this.newFileName, FileMode.CreateNew))
using (GZipStream zipStream = new GZipStream(ms, CompressionMode.Compress, false))
{
zipStream.Write(bytes, 0, bytes.Length);
}
}
}
In regards to the source code, this.fileName = c:\Audio.wav
and the newFileName
is c:\Audio.wav.gz
(but have also tried c:\audio.gz
)
Upvotes: 19
Views: 63722
Reputation: 9456
I use this class for compressing/decompressing:
internal class GZipProcessor : IZipProcessor
{
public byte[] Compress(byte[] data)
{
using (var compressedStream = new MemoryStream())
{
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Compress))
{
zipStream.Write(data, 0, data.Length);
zipStream.Close();
return compressedStream.ToArray();
}
}
}
public byte[] Decompress(byte[] data)
{
using (var compressedStream = new MemoryStream(data))
{
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
{
using (var resultStream = new MemoryStream())
{
zipStream.CopyTo(resultStream);
return resultStream.ToArray();
}
}
}
}
}
And how i can use it:
public void Compress(string inputPath, string outputPath)
{
byte[] originalBytes = File.ReadAllBytes(inputPath);
byte[] zippedBytes = base.ZipProcessor.Compress(originalBytes);
File.WriteAllBytes(outputPath, zippedBytes);
}
public void Decompress(string inputPath, string outputPath)
{
byte[] zippedBytes = File.ReadAllBytes(inputPath);
byte[] originalBytes = base.ZipProcessor.Decompress(zippedBytes);
File.WriteAllBytes(outputPath, originalBytes);
}
also i have a github repository with more complex code
Upvotes: 14
Reputation: 12858
When you use GzipStream
or DeflateStream
from the System.IO.Compression
namespace, the Stream
you supply in the constructor will be written to for compression and read from in decompression.
Since you are trying to compress the data here, using the MemoryStream
is incorrect as you are not trying to compress to it, but rather use it as a data source. So your MemoryStream
should be the input Stream
and the FileStream
is your output.
I highly recommend you using MemoryStream
as a data source over the raw byte[]
because Stream
has a lot more versatility and application (FileStream
, NetworkStream
, CryptoStream
, etc.)
Here are some examples using the async
/await
pattern:
public static async Task CompressToFileAsync(byte[] buffer,
string outputFile)
{
using (var inputStream = new MemoryStream(buffer))
await CompressToFileAsync(inputStream, outputFile);
}
public static async Task CompressToFileAsync(Stream inputStream,
string outputFile)
{
using (var outputStream = File.Create(outputFile))
using (var gzip = new GZipStream(outputStream, CompressionMode.Compress))
{
await inputStream.CopyToAsync(gzip);
gzip.Close();
}
}
public static async Task<MemoryStream> DecompressFromFileAsync(string inputFile)
{
var outputStream = new MemoryStream();
using (var inputStream = File.Open(inputFile, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var gzip = new GZipStream(inputStream, CompressionMode.Decompress))
{
await gzip.CopyToAsync(outputStream);
gzip.Close();
inputStream.Close();
// After writing to the MemoryStream, the position will be the size
// of the decompressed file, we should reset it back to zero before returning.
outputStream.Position = 0;
return outputStream;
}
}
NOTE: Always call GzipStream.Close()
before you close the input or output Stream
. It does some final buffer flushing when closing/disposed and if the input or output is closed first it will throw an exception when it tries to do so. (This also applies to DeflateStream
)
Upvotes: 14
Reputation: 35477
bytes
already has the data to compress. ms.ReadByte()
should not be used. zipStream
the output file stream should be used.Try this:
var bytes = File.ReadAllBytes(this.fileName);
using (FileStream fs =new FileStream(this.newFileName, FileMode.CreateNew))
using (GZipStream zipStream = new GZipStream(fs, CompressionMode.Compress, false))
{
zipStream.Write(bytes, 0, bytes.Length);
}
EDIT
The original code creates a zero length file because you do not write to the file stream.
Upvotes: 19
Reputation: 3553
why FileStream works and memory doesn't
Because backing store of MemoryStream is your RAM memory, not hard drive. So when you passing MemoryStream
object to GZipStream
constructor, you gzip will be just in RAM.
With this code:
using (FileStream fs =new FileStream(this.newFileName, FileMode.CreateNew))
you are creating new FileStream, but you did not use it. To store gzip using this FIleStream, you need to pass it to GZipStream
constructor, to make it backing store to be hard drive.
Upvotes: 3