GZipStream delivers zero byte file - c#

I am using DotNetZip's GZipStream to zip a file. The problem I have is that the resulting file is empty. I tried flushing/closing streams, but without result. Anyone knows what I do wrong:
using (var outputStream = new FileStream(path + fileName + ".gz", FileMode.Create, FileAccess.Write, FileShare.None))
{
using (var zipStream = new GZipStream(outputStream, CompressionMode.Compress))
{
using (var inputStream = new FileStream(path + fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
await inputStream.CopyToAsync(zipStream);
}
}
}

Works fine here; do you have a fully reproducible example, perhaps based on this one?
Results:
dummy.txt:6492 bytes
Waiting for completion (don't do this in real code, ever)...
Complete
dummy.txt.gz:512 bytes
Code:
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
static class P
{
static void Main()
{
File.WriteAllLines("dummy.txt",
Enumerable.Range(1, 200).Select(i => "this is some contents: line " + i));
WriteSize("dummy.txt");
var t = Task.Run(() => DoTheThing(Environment.CurrentDirectory + "\\", "dummy.txt"));
Console.WriteLine("Waiting for completion (don't do this in real code, ever)...");
t.Wait();
Console.WriteLine("Complete");
WriteSize("dummy.txt.gz");
}
private static void WriteSize(string path)
{
var file = new FileInfo(path);
Console.WriteLine(path + ":" + file.Length + " bytes");
}
async static Task DoTheThing(string path, string fileName)
{
using (var outputStream = new FileStream(path + fileName + ".gz", FileMode.Create, FileAccess.Write, FileShare.None))
{
using (var zipStream = new GZipStream(outputStream, CompressionMode.Compress))
{
using (var inputStream = new FileStream(path + fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
await inputStream.CopyToAsync(zipStream);
}
}
}
}
}

Related

Invalid zip file reading from a Stream in C#

I have the following code:
private static byte[] ConverterStringToByte(Stream body)
{
string fileName = "data_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
// Take out the bytes from the memory stream and safely close the stream
using (var ms = new MemoryStream())
{
body.CopyTo(ms);
using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, false))
{
var zipEntry = zipArchive.CreateEntry(fileName, CompressionLevel.Optimal);
using (BinaryWriter writer = new BinaryWriter(zipEntry.Open()))
{
ms.Position = 0;
writer.Write(ms.ToArray());
}
}
return ms.ToArray();
}
}
I am downloading the file successfully, however I'm getting
invalid file
when trying to open
I think it should be something like this. Not sure about fileName though, because it's the name of the file being put into archive, so I don't think it should have *.zip extension. Unless you are creating a zip of zips.
static byte[] ConverterStringToByte(Stream body)
{
string fileName = #"data_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var ms = new MemoryStream())
{
using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, false))
{
var zipEntry = zipArchive.CreateEntry(fileName, CompressionLevel.Optimal);
using (var destStream = zipEntry.Open())
{
body.CopyTo(destStream);
}
}
return ms.ToArray();
}
}

Getting extra space in last line after file seek and zipping it

Using below code I am using File seek and convert to result byte to compressed stream and generating the zip file,
public static async Task Get(string filename)
{
byte[] result;
byte[] compressedBytes;
using (FileStream SourceStream = File.Open(filename, FileMode.Open))
{
SourceStream.Seek(20, SeekOrigin.Begin);
result = new byte[SourceStream.Length];
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
}
string fileName = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var outStream = File.Create(fileName))
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry("test.txt", CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(result))
{
fileToCompressStream.CopyTo(entryStream);
}
}
}
}
Now when I unzip the resultant file has extra space. What's the reason for it and how to resolve it?
You're seeking 20 bytes into the stream, but the length of your array is the complete length of the stream. Therefore the final 20 bytes in your array are being ignored.
The simple fix for this is just to allocate less space, and then only ask to read the reduced number of bytes:
result = new byte[SourceStream.Length - 20];
await SourceStream.ReadAsync(result, 0, result.Length);
Note that you're also assuming that a single call to ReadAsync will read all the data. That may be the case in many situations, but it's generally not a good idea to assume that about streams.
It would be simpler just to copy straight from the file stream to the compressed stream though, instead of reading the whole file into memory first:
public static async Task Get(string filename)
{
string outputFile = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var outStream = File.Create(outputFile))
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry("test.txt", CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = File.Open(filename, FileMode.Open))
{
// Skip the first 20 bytes
fileToCompressStream.Position = 20;
fileToCompressStream.CopyTo(entryStream);
}
}
}
}

c# Compress File System out of memory

I'm developing a service to compress some files and I have been doing tests to the service and it is getting a major failure in bigger files. I'm using an outlook file with 6GB to test and I get an out of memory error after compressing 500Mb.
This is my code:
using (FileStream zipToOpen = new FileStream(#dir + ZipName, FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
foreach (string file in files)
{
if (File.GetCreationTime(#dir + file).AddSeconds(FileAge) < DateTime.Now)
{
ZipArchiveEntry fileEntry = archive.CreateEntry(file);
using (BinaryWriter writer = new BinaryWriter(fileEntry.Open()))
{
using (FileStream sr = new FileStream(#dir + file, FileMode.Open, FileAccess.Read))
{
byte[] block = new byte[1024];
int bytesRead = 0;
while ((bytesRead = sr.Read(block,0, block.Length)) >0)
{
writer.Write(block, 0, bytesRead);
}
}
}
File.Delete(#dir + file);
}
}
}
}
Any ideia how I can solve it?
Thank you in advance

Write from a stream to a string

I'm trying to use the streamwriter to write into a file that is created temporarily i.e. _logFileName and at the same time write the data written into the file to a string using stream reader. The current code shows no errors but at runtime says that it can not read from _logFileName as it is in use already.
how to do i do this ?
using (StreamWriter _logFile = File.CreateText(_logFileName))
{
//string s = "";
//using (StreamReader fill_log = new StreamReader(s))
using (StreamReader fill_log = new StreamReader(_logFileName))
{
_logFile.WriteLine("Logfile name is: " + _logFileName);
content += fill_log.ReadLine();
_logFile.WriteLine("LOG FILE STARTED AT: " + _startDateTime.ToString());
content += fill_log.ReadLine();
_logFile.WriteLine("============================================");
content += fill_log.ReadLine();
_logFile.Write(_message);
content += fill_log.ReadLine();
_logFile.WriteLine();
content += fill_log.ReadLine();
}
_logFile.Close();
}
So based on the suggestion i changed the code to this:
using (var fsWrite = new FileStream(_logFileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
using (var _logFile = new StreamWriter(fsWrite))
using (var fsRead = new FileStream(_logFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var fill_log = new StreamReader(fsRead))
{
_logFile.WriteLine();
content += fill_log.ReadLine();
_logFile.WriteLine("TIME OF LOG ENTRY: " + DateTime.Now);
content += fill_log.ReadLine();
// Arbitrary objects can also be written to the file.
_logFile.WriteLine(_message);
content += fill_log.ReadLine();
_logFile.Flush();
_logFile.Close();
On doing so, i am able to red and write simultaneously! that gave no problem. Thanks. But the content string variable seems to end after everyright. and ideas why this would happen ?
In order to be able to simultaneously read and write from the same file you have to create the FileStream object manually using one of the constructors that take a FileShare parameter, for example this one.
using (var fsWrite = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
using (var _logFile = new StreamWriter(fsWrite))
using (var fsRead = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var fill_log = new StreamReader(fsRead))
{
...
}
Another way to achieve what you want is using a specialized TextWriter that writes the StreamWriter and a StringBuilder:
using (StreamWriter _logFile = File.CreateText(_logFileName))
{
using (var builder = new StringBuildingStreamWriter(_logFile))
{
builder.WriteLine("Logfile name is: " + _logFileName);
builder.WriteLine("LOG FILE STARTED AT: " + _startDateTime.ToString());
builder.WriteLine("============================================");
builder.Write(_message);
builder.WriteLine();
content += builder.ToString();
}
_logFile.Close();
}
public class StringBuildingStreamWriter:TextWriter
{
StringBuilder sb = new StringBuilder();
private StreamWriter sw;
public StringBuildingStreamWriter(StreamWriter sw)
{
this.sw = sw;
}
public override void WriteLine(string value)
{
sb.AppendLine(value);
sw.WriteLine(value);
}
public override void WriteLine()
{
sw.WriteLine();
sb.AppendLine();
}
public override void Write(string value)
{
sb.Append(value);
sw.Write(value);
}
public override string ToString()
{
return sb.ToString();
}
public override Encoding Encoding
{
get { return UTF8Encoding.UTF8; }
}
}

Reading a memorystream

Using several examples here on StackOverflow I thought the following code would decompress a gzip file then read the memory-stream and write it's content to the console. No errors occur but I get no output.
public static void Decompress(FileInfo fileToDecompress)
{
using (FileStream originalFileStream = fileToDecompress.OpenRead())
{
string currentFileName = fileToDecompress.FullName;
string newFileName = currentFileName.Remove(currentFileName.Length - fileToDecompress.Extension.Length);
using (FileStream decompressedFileStream = File.Create(newFileName))
{
using (GZipStream decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress))
{
MemoryStream memStream = new MemoryStream();
memStream.SetLength(decompressedFileStream.Length);
decompressedFileStream.Read(memStream.GetBuffer(), 0, (int)decompressedFileStream.Length);
memStream.Position = 0;
var sr = new StreamReader(memStream);
var myStr = sr.ReadToEnd();
Console.WriteLine("Stream Output: " + myStr);
}
}
}
}
You are trying to copy an empty stream. "decompressedFileStream" is created by File.Create(), so it's empty. Swap "decompressedFileStream" to "decompressionStream" and you will be able to see your file content into "myStr".
public static void Decompress(FileInfo fileToDecompress)
{
using (FileStream originalFileStream = fileToDecompress.OpenRead())
{
string currentFileName = fileToDecompress.FullName;
string newFileName = currentFileName.Remove(currentFileName.Length - fileToDecompress.Extension.Length);
using (FileStream decompressedFileStream = File.Create(newFileName))
{
using (GZipStream decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress))
{
MemoryStream memStream = new MemoryStream();
//memStream.SetLength(decompressedFileStream.Length); not necessary
decompressionStream.CopyTo(memStream);
memStream.Seek(0, SeekOrigin.Begin);
var sr = new StreamReader(memStream);
var myStr = sr.ReadToEnd();
Console.WriteLine("Stream Output: " + myStr);
}
}
}
}
Try this snippets. I use CopyTo instead of Read to copy the data to the memory stream and I use Seek() method instead of Position to return at the start of the memory stream.

Categories