I am using the following C# code to compress a file:
// Open the stream we want to compress
FileStream fs = File.Create(#"C:\Projects\Samples\test\compressed.zip", 0);
// Creates the GZipStream
GZipStream gzip = new GZipStream(fs, CompressionMode.Compress);
// Reading the content to compress
byte[] bytes = File.ReadAllBytes(#"C:\Projects\Samples\samplefile.xml");
// Writing compressed content
gzip.Write(bytes, 0, bytes.Length);
gzip.Close(); // This also closes the FileStream (the underlying stream)
However, when I extract the file from windows explorer the file loses it's extension so instead of samplefile.xml it just becomes samplefile. Same thing happened with .txt file not just .xml file.
Can you help me see what I'm doing wrong?
ok found the problem:
Line 2 has to be as follows:
FileStream fs = File.Create(#"C:\Projects\Samples\test\compressed.xml.zip", 0);
GZipStream doesn't create zip archives. It creates a gzip file, which contains only one file, and doesn't necessarily store a filename at all. Normally you should use the .gz extension to identify a gzip file, and it's conventional to use the entire name of the original file with .gz appended on the end. See also here for more information about gzip format: http://en.wikipedia.org/wiki/Gzip#File_format
If you actually want to create zip archives, you might want to use a library like SharpZipLib: http://www.icsharpcode.net/opensource/sharpziplib/
Related
I'm using OpenXML to generate an Excel spreadsheet.
I'm generating the spreadsheet in a MemoryStream; the caller is writing writing out the actual file. For example, my .Net Core controller will return the memory stream as a FileResult. At the moment, I've got a standalone Console mode program that's writing a FileStream.
PROBLEM: I'm getting extra bytes at the end of the file. Since an OpenXml .xlsx file is a .zip file, the extra bytes effectively corrupt the file.
Program.cs:
using (MemoryStream memoryStream = new MemoryStream())
{
OpenXMLGenerate(memoryStream, sampleData);
long msPos = memoryStream.Position; // Position= 1869: Good!
memoryStream.Position = 0;
using (FileStream fs = new FileStream("myfile.xlsx", FileMode.OpenOrCreate))
{
memoryStream.WriteTo(fs);
long fsPos = fs.Position; // Position= 1869: Good!
}
// Myfile.xlsx filesize= 2014, not 1869! Bad!!!
}
When I open the file in 7-Zip, it it says:
Warnings: There are some data after the end of the payload data
Physical Size: 1869
Tail Size: 145
When I try to open it as a .zip file, Windows says:
The Compressed (zipped) folder is invald.
Q: Any idea why I'm getting a 2014 byte file, instead of 1869 bytes?
Q: What can I do about it?
(Documenting per comments.) The issue could be explained by the file replacing an existing file of length 2014 bytes.
Creating a file stream using a mode of FileMode.OpenOrCreate is equivalent to using FileMode.Open if the referenced file exists. If the length of the memory stream is less than the length of the existing file, the existing file will not be truncated to the length of the memory stream; in this case, if N is the length of the memory stream, the first N bytes of the existing file will be overwritten with the contens of the memory stream, and the remaining bytes will persist from the original file.
Creating the file stream with a file mode of FileMode.Create will replace the existing file entirely (if one exists), eliminating any possibility that the new file will contain remnants of the existing file.
I have a service which executes a request to a client api that returns a octet-stream response body with the Content-Disposition header in it (This api is meant to return a zip file.). I am using RestSharp and the DownloadData function to get the response as a byte array, but I want to then save the zip file to my local server.
I have tried using DotNetZip and a MemoryStream to create the zip file by using the following example:
using (MemoryStream stream = new MemoryStream(fileBytes))
{
using (ZipFile zip = new ZipFile())
{
zip.AddEntry("test", stream);
zip.Save(filePath);
}
}
The code above creates a zip file and an entry called test but I cannot open it.
Just to clarify, the zip file I am trying to create contains the following files and folders:
296927a0-5ac7-4ccd-9928-bd74ef7ae68a_20200227074457 (Root folder)
images (folder)
jpg image
jpg image
jpg image
details.json (file)
achievements.json (file)
evaluation.json (file)
Is there any way to achieve this?
As I understood the fileBytes is already a zip file byte stream, what means you don't need to zip it again. Just save as file.
File.WriteAllBytes(filePath, fileBytes);
For anyone else who encounters this problem, as in saving the byte array to disk and getting a corrupted zip file, I have figured out the answer with the help of Dennis Tretyakov.
Basically, when downloading a octet-stream, the first section of is dedicated to the structure of the zip file. In my case the first section of the zip file was:
--c7c2ad44-881c-47c4-89e5-323f91b75269
Content-Disposition: form-data; name="deb08d79-372d-4de2-a684-1298f25fecd9_20200310142909.zip"
Content-Type: application/octet-stream
And thereafter the actual data for the zip file appears after a linebreak.
What I ended up doing was calculating the byte count of the "header" section by converting the byte array to a string:
string stringEncodedBytes = Encoding.ASCII.GetString(response.RawBytes);
Then with knowing the starting point of the zip file data, finding the index of the starting point:
int headerSectionIndex = stringEncodedBytes.IndexOf("PK");
Once I found the index, which in my case always seems to be 178, I simple removed that part of the byte array and then copy the trimmed byte array to a new byte array and write it to disk:
byte[] trimmedFileBytes = new byte[response.RawBytes.Length - headerSectionIndex];
Array.Copy(response.RawBytes, headerSectionIndex, trimmedFileBytes, 0, trimmedFileBytes.Length);
File.WriteAllBytes(filePath, trimmedFileBytes);
I would suggest that anyone struggling with the same issue, open the "corrupted" zip file in notepad++ and take a look at the first section and then determine where the data section of the zip file starts.
The initial zip file data looks like this:
And the trimmed zip file data looks like this:
So i had the same issue Dev_101 had, in which a RestResponse was corrupting the .zip file, however in my case, it was just appending an extra " on the beginning and end, so I just needed to remove the " and Base64Decode it and saved perfectly.
I am creating two gzip files, one which contains only a single gzip member, where as the second one contains 2 gzip members (two files concatenated into a single gzip file).
When I try to download this through a web server, chrome decompresses the first file fine and shows the contents as is.
But when downloading the second file, chrome seems to decompress only the first member and the second member is left uncompressed.
I tried opening the second file using 7zip and verified that the contents are fine.
Below is the code I am using to generate the two files in C#
FileStream fileStream = new FileStream("D:\\single_big_gzip.gz", FileMode.Create);
FileStream fileStream2 = new FileStream("D:\\multiple_gzip_files.gz", FileMode.Create);
// Write to First file
GZipStream gzipStream = new GZipStream(fileStream, CompressionMode.Compress, false);
StreamWriter writer = new StreamWriter(gzipStream);
writer.WriteLine("hello world!");
writer.Flush();
gzipStream.Close();
fileStream.Close();
// write to second file
gzipStream = new GZipStream(fileStream2, CompressionMode.Compress, true);
writer = new StreamWriter(gzipStream);
writer.WriteLine("Hello world!");
writer.Flush();
gzipStream.Close();
gzipStream = new GZipStream(fileStream2, CompressionMode.Compress, true);
writer = new StreamWriter(gzipStream);
writer.WriteLine("Bye Bye Birdie!");
writer.Flush();
gzipStream.Close();
fileStream2.Close();
Any idea how I can get chrome to decompress the entire second file?
Thank you.
Though RFC 1952 specifies that "A gzip file consists of a series of "members" (compressed data sets).", and though gzip properly decodes such streams, browsers generally are not compliant with the standard and will only decode the first member.
I've basically copied this code sample directly from msdn with some minimal changes. The CopyTo method is silently failing and I have no idea why. What would cause this behavior? It is being passed a 78 KB zipped folder with a single text file inside of it. The returned FileInfo object points to a 0 KB file. No exceptions are thrown.
public static FileInfo DecompressFile(FileInfo fi)
{
// Get the stream of the source file.
using (FileStream inFile = fi.OpenRead())
{
// Get original file extension,
// for example "doc" from report.doc.cmp.
string curFile = fi.FullName;
string origName = curFile.Remove(curFile.Length
- fi.Extension.Length);
//Create the decompressed file.
using (FileStream outFile = File.Create(origName))
{
// work around for incompatible compression formats found
// here http://george.chiramattel.com/blog/2007/09/deflatestream-block-length-does-not-match.html
inFile.ReadByte();
inFile.ReadByte();
using (DeflateStream Decompress = new DeflateStream(inFile,
CompressionMode.Decompress))
{
// Copy the decompression stream
// into the output file.
Decompress.CopyTo(outFile);
return new FileInfo(origName);
}
}
}
}
In a comment you say that you are trying to decompress a zip file. The DeflateStream class can not be used like this on a zip file. The MSDN example you mentioned uses DeflateStream to create individual compressed files and then uncompresses them.
Although zip files might use the same algorithm (not sure about that) they are not just compressed versions of a single file. A zip file is a container that can hold many files and/or folders.
If you can use .NET Framework 4.5 I would suggest to use the new ZipFile or ZipArchive class. If you must use an earlier framework version there are free libraries you can use (like DotNetZip or SharpZipLib).
I have been developing an web application with Asp.Net and I'm using SharpZipLib to work with odt files (from Open Office) and in the future docx files (for ms office). I need to open an odt file (like a zip file) change a xml file inside it, zip again and give it to the browser send to my client.
I can do this in file system but it will get a space in my disk temporarily and we don't want it. I would like to do this in memory (with a MemoryStream class), but I don't know how to unzip folders/files in a memory stream with SharpZipLib, change and use it to zip again. Is there any sample about how to do this?
Thank you
You can use something like
Stream inputStream = //... File.OpenRead(...);
//for read file
ZipInputStream zipInputStream = new ZipInputStream(inputStream));
//for output
MemoryStream memoryStream = new MemoryStream();
using ( ZipOutputStream zipStream = new ZipOutputStream(memoryStream))
{
ZipEntry entry = new ZipEntry("...");
//...
zipStream.PutNextEntry(entry);
zipStream.Write(data, 0, data.Length);
//...
zipStream.Finish();
zipStream.Close();
}
Edit ::
You need in general unZip your file, get ZipEntry , change , and write in ZipOutputStream with MemoryStream.
Use this article http://www.codeproject.com/KB/cs/Zip_UnZip.aspx