DeflateStream / GZipStream to CryptoStream and vice versa - c#

I want to to compress and encrypt a file in one go by using this simple code:
public void compress(FileInfo fi, Byte[] pKey, Byte[] pIV)
{
// Get the stream of the source file.
using (FileStream inFile = fi.OpenRead())
{
// Create the compressed encrypted file.
using (FileStream outFile = File.Create(fi.FullName + ".pebf"))
{
using (CryptoStream encrypt = new CryptoStream(outFile, Rijndael.Create().CreateEncryptor(pKey, pIV), CryptoStreamMode.Write))
{
using (DeflateStream cmprss = new DeflateStream(encrypt, CompressionLevel.Optimal))
{
// Copy the source file into the compression stream.
inFile.CopyTo(cmprss);
Console.WriteLine("Compressed {0} from {1} to {2} bytes.", fi.Name, fi.Length.ToString(), outFile.Length.ToString());
}
}
}
}
}
The following lines will restore the encrypted and compressed file back to the original:
public void decompress(FileInfo fi, Byte[] pKey, Byte[] pIV)
{
// Get the stream of the source file.
using (FileStream inFile = fi.OpenRead())
{
// Get original file extension, for example "doc" from report.doc.gz.
String curFile = fi.FullName;
String origName = curFile.Remove(curFile.Length - fi.Extension.Length);
// Create the decompressed file.
using (FileStream outFile = File.Create(origName))
{
using (CryptoStream decrypt = new CryptoStream(inFile, Rijndael.Create().CreateDecryptor(pKey, pIV), CryptoStreamMode.Read))
{
using (DeflateStream dcmprss = new DeflateStream(decrypt, CompressionMode.Decompress))
{
// Copy the uncompressed file into the output stream.
dcmprss.CopyTo(outFile);
Console.WriteLine("Decompressed: {0}", fi.Name);
}
}
}
}
}
This works also with GZipStream.

A decompressing stream is expected to be read from, not written to. (unlike a CryptoStream, which supports all four combinations of read/write and encrypt/decrypt)
You should create the DeflateStream around a CryptoStreamMode.Read stream around the input file, then copy from that directly to the output stream.

Related

Create zip file in memory from bytes (text with arbitrary encoding)

The application i'm developing needs to compress xml files into zip files and send them through http requests to a web service. As I dont need to keep the zip files, i'm just performing the compression in memory. The web service is denying my requests because the zip files are apparently malformed.
I know there is a solution in this question which works perfectly, but it uses a StreamWriter. My problem with that solution is that StreamWriter requires an encoding or assumes UTF-8, and I do not need to know the enconding of the xml files. I just need to read the bytes from those files, and store them inside a zip file, whatever encoding they use.
So, to be clear, this question has nothing to do with encodings, as I don't need to transform the bytes into text or the oposite. I just need to compress a byte[].
I'm using the next code to test how my zip file is malformed:
static void Main(string[] args)
{
Encoding encoding = Encoding.GetEncoding("ISO-8859-1");
string xmlDeclaration = "<?xml version=\"1.0\" encoding=\"" + encoding.WebName.ToUpperInvariant() + "\"?>";
string xmlBody = "<Test>ª!\"·$%/()=?¿\\|##~€¬'¡º</Test>";
string xmlContent = xmlDeclaration + xmlBody;
byte[] bytes = encoding.GetBytes(xmlContent);
string fileName = "test.xml";
string zipPath = #"C:\Users\dgarcia\test.zip";
Test(bytes, fileName, zipPath);
}
static void Test(byte[] bytes, string fileName, string zipPath)
{
byte[] zipBytes;
using (var memoryStream = new MemoryStream())
using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: false))
{
var zipEntry = zipArchive.CreateEntry(fileName);
using (Stream entryStream = zipEntry.Open())
{
entryStream.Write(bytes, 0, bytes.Length);
}
//Edit: as the accepted answer states, the problem is here, because i'm reading from the memoryStream before disposing the zipArchive.
zipBytes = memoryStream.ToArray();
}
using (var fileStream = new FileStream(zipPath, FileMode.OpenOrCreate))
{
fileStream.Write(zipBytes, 0, zipBytes.Length);
}
}
If I try to open that file, I get an "Unexpected end of file" error. So apparently, the web service is correctly reporting a malformed zip file. What I have tried so far:
Flushing the entryStream.
Closing the entryStream.
Both flushing and closing the entryStream.
Note that if I open the zipArchive directly from the fileStream the zip file is formed with no errors. However, the fileStream is just there as a test, and I need to create my zip file in memory.
You are trying to get bytes from MemoryStream too early, ZipArchive did not write them all yet. Instead, do like this:
using (var memoryStream = new MemoryStream()) {
// note "leaveOpen" true, to not dispose memoryStream too early
using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: true)) {
var zipEntry = zipArchive.CreateEntry(fileName);
using (Stream entryStream = zipEntry.Open()) {
entryStream.Write(bytes, 0, bytes.Length);
}
}
// now, after zipArchive is disposed - all is written to memory stream
zipBytes = memoryStream.ToArray();
}
If you use a memory stream to load your text you can control the encoding type and it works across a WCF service. This is the implementation i am using currently and it works on my WCF services
private byte[] Zip(string text)
{
var bytes = Encoding.UTF8.GetBytes(text);
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(mso, CompressionMode.Compress))
{
CopyTo(msi, gs);
}
return mso.ToArray();
}
}
private string Unzip(byte[] bytes)
{
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(msi, CompressionMode.Decompress))
{
CopyTo(gs, mso);
}
return Encoding.UTF8.GetString(mso.ToArray());
}
}

Using MemoryStream and DotNetZip to zip a json file

I have a JSON file created, and I am going to zip it using DotNetZip.
Using with StreamWriter to zip it is working, if I try to use MemoryStream it will not working.
StreamWriter :
sw = new StreamWriter(assetsFolder + #"manifest.json");
sw.Write(strManifest);
sw.Close();
zip.AddFile(Path.Combine(assetsFolder, "manifest.json"), "/");
zip.AddFile(Path.Combine(assetsFolder, "XXXXXXX"), "/");
zip.Save(outputStream);
MemoryStream :
var manifestStream = GenerateStreamFromString(strManifest);
public static Stream GenerateStreamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
zip.AddEntry("manifest.json", manifestStream);
zip.AddFile(Path.Combine(assetsFolder, "XXXXXXX"), "/");
zip.Save(outputStream);
I must using the .JSON file type to zip it, Can any one told me where have a mistake?
To create a Gzipped Json you need to use GZipStream. Try method below.
https://www.dotnetperls.com/gzipstream
GZipStream compresses data. It saves data efficiently—such as in
compressed log files. We develop a utility method in the C# language
that uses the System.IO.Compression namespace. It creates GZIP files.
It writes them to the disk.
public static void CompressStringToFile(string fileName, string value)
{
// A.
// Write string to temporary file.
string temp = Path.GetTempFileName();
File.WriteAllText(temp, value);
// B.
// Read file into byte array buffer.
byte[] b;
using (FileStream f = new FileStream(temp, FileMode.Open))
{
b = new byte[f.Length];
f.Read(b, 0, (int)f.Length);
}
// C.
// Use GZipStream to write compressed bytes to target file.
using (FileStream f2 = new FileStream(fileName, FileMode.Create))
using (GZipStream gz = new GZipStream(f2, CompressionMode.Compress, false))
{
gz.Write(b, 0, b.Length);
}
}

Create in memory zip from a file

Is DeflateStream supposed to create archived stream that can be stored as standard .zip archive?
I'm trying to create in-memory zip (to be sent remotely) from a local file.
I used a DeflateStream to get a compressed byte array from the file on local disk:
public static byte[] ZipFile(string csvFullPath)
{
using (FileStream csvStream = File.Open(csvFullPath, FileMode.Open, FileAccess.Read))
{
using (MemoryStream compressStream = new MemoryStream())
{
using (DeflateStream deflateStream = new DeflateStream(compressStream, CompressionLevel.Optimal))
{
csvStream.CopyTo(deflateStream);
deflateStream.Close();
return compressStream.ToArray();
}
}
}
}
This works great.
However when I dump the resulting bytes to a zip file:
byte[] zippedBytes = ZipFile(FileName);
File.WriteAllBytes("Sample.zip", zippedBytes);
I cannot open the resulting .zip archive with windows build-in .zip functionality (or with any other 3rd party archive tool).
An alternative I'm planning now is using ZipArchive - however that would require creating temporary files on disk (first copy the file into separate directory, then zip it, then read it into byte array and then delete it)
You can use this nice library https://dotnetzip.codeplex.com/
or you can use ZipArchive and it works with MemoryStream pretty good:
public static byte[] ZipFile(string csvFullPath)
{
using (FileStream csvStream = File.Open(csvFullPath, FileMode.Open, FileAccess.Read))
{
using (MemoryStream zipToCreate = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(zipToCreate, ZipArchiveMode.Create, true))
{
ZipArchiveEntry fileEntry = archive.CreateEntry(Path.GetFileName(csvFullPath));
using (var entryStream = fileEntry.Open())
{
csvStream.CopyTo(entryStream);
}
}
return zipToCreate.ToArray();
}
}
}

How to compress a file using GZipStream in C# in .NET 2.0

I want to compress a file that has binary data and save the compressed data in another file:
FileStream fileStream = new FileStream("compressed_file.bin", FileMode.Create, FileAccess.Write);
GZipStream compressionStream = new GZipStream(fileStream, CompressionMode.Compress);
StreamWriter writer = new StreamWriter(compressionStream);
writer.Write(File.ReadAllBytes("file_to_be_compressed.bin"), 0, File.ReadAllBytes("file_to_be_compressed.bin").Length);
writer.Close();
I get following error:
cannot convert from 'byte[]' to 'char[]'
in line:
writer.Write(File.ReadAllBytes("file_to_be_compressed.bin"), 0, File.ReadAllBytes("file_to_be_compressed.bin").Length)
And is it fine to convert the binary data of file to byte array, or is it better to pass binary data of file as stream?
Note: CopyTo is not available in .NET 2.0
Try this, according to http://www.dotnetperls.com/gzipstream
using System.IO;
using System.IO.Compression;
using System.Text;
class Program
{
static void Main()
{
try
{
// 1.
// Starting file is 26,747 bytes.
string anyString = File.ReadAllText("TextFile1.txt");
// 2.
// Output file is 7,388 bytes.
CompressStringToFile("new.gz", anyString);
}
catch
{
// Could not compress.
}
}
public static void CompressStringToFile(string fileName, string value)
{
// A.
// Write string to temporary file.
string temp = Path.GetTempFileName();
File.WriteAllText(temp, value);
// B.
// Read file into byte array buffer.
byte[] b;
using (FileStream f = new FileStream(temp, FileMode.Open))
{
b = new byte[f.Length];
f.Read(b, 0, (int)f.Length);
}
// C.
// Use GZipStream to write compressed bytes to target file.
using (FileStream f2 = new FileStream(fileName, FileMode.Create))
using (GZipStream gz = new GZipStream(f2, CompressionMode.Compress, false))
{
gz.Write(b, 0, b.Length);
}
}
}

how to compress and uncompress a text file

I am having two problems
My problems are
My file name is 20110505.txt , and it is compressing a zip file name as -> 20110505.txt.zip
But I need after compressing this file 20110505.txt as --> 20110505.zip only.
I am using this dll
using System.IO.Compression;
Here is my code for compress,
1)is my text format
string path = DayDestination + "\\" + txtSelectedDate.Text + ".txt";
StreamWriter Strwriter = new StreamWriter(path);
DirectoryInfo di = new DirectoryInfo(path);
FileInfo fi = new FileInfo(path);
Compress(fi);
public static void Compress(FileInfo fi)
{
// Get the stream of the source file.
using (FileStream inFile = fi.OpenRead())
{
// Prevent compressing hidden and already compressed files.
if ((File.GetAttributes(fi.FullName) & FileAttributes.Hidden)
!= FileAttributes.Hidden & fi.Name != ".zip")
{
// Create the compressed file.
using (FileStream outFile = File.Create(fi.FullName + ".zip"))
//using (FileStream outFile = File.Create( fi.Name+ ".zip"))
{
using (GZipStream Compress = new GZipStream(outFile,
CompressionMode.Compress))
{
// Copy the source file into the compression stream.
byte[] buffer = new byte[4096];
int numRead;
while ((numRead = inFile.Read(buffer, 0, buffer.Length)) != 0)
{
Compress.Write(buffer, 0, numRead);
}
Console.WriteLine("Compressed {0} from {1} to {2} bytes.",
fi.Name, fi.Length.ToString(), outFile.Length.ToString());
}
}
}
}
}
If my zip file name is 20110505.zip after uncompressing i want my file name to be 20110505.txt . after uncomressing the zip file to text file i want to delete the zip file after compressing
string Path2 = (string)(Application.StartupPath + "\\TEMP\\" + "\\" + name_atoz);
DirectoryInfo di = new DirectoryInfo(Path2);
FileInfo fi = new FileInfo(Path2);
Compress(fi);
public static void Decompress(FileInfo fi)
{
// Get the stream of the source file.
using (FileStream inFile = fi.OpenRead())
{
// Get original file extension, for example "doc" from report.doc.gz.
string curFile = fi.FullName;
string origName = curFile.Remove(curFile.Length - fi.Extension.Length);
//Create the decompressed file.
using (FileStream outFile = File.Create(origName))
{
using (GZipStream Decompress = new GZipStream(inFile,
CompressionMode.Decompress))
{
//Copy the decompression stream into the output file.
byte[] buffer = new byte[4096];
int numRead;
while ((numRead = Decompress.Read(buffer, 0, buffer.Length)) != 0)
{
outFile.Write(buffer, 0, numRead);
}
Console.WriteLine("Decompressed: {0}", fi.Name);
}
}
}
}
I want this because i am creating a project which reads the text file .
Is there any suggestion for my problem.
Thanks In Advance
As Mitch pointed out in the comments above, the problem seems to be with your file name being used for the zip file. You include the FullName, but that has the .txt at the end. Trim that off and you should have what you want.
The line I'm talking about is this one:
using (FileStream outFile = File.Create(fi.FullName + ".zip"))
A simple way to fix it would be as follows:
using (FileStream outFile = File.Create(System.Text.RegularExpressions.Regex.Replace(fi.FullName, ".txt$", "") + ".zip"))
To delete your zip file after you decompress it, put the following line in your Decompress method as the very last line (outside your outer-most using statement):
File.Delete(fi);
You probably want to wrap that in a try-catch but this is the basic code to run. Here is an article on how to delete a file safely:
http://www.dotnetperls.com/file-delete

Categories