C# api octet-stream response body to zip file - c#

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.

Related

decompress files on google storage on the fly using c#

I have a very interesting problem that I hope I can solve using .Net, simply I have a zip file in google storage which I want to decompress and move to a different bucket, but I don't have enough memory nor storage to save the whole file and decompress. To solve this issue I have to read the central directory part of the zip file at the end of the file and then do streaming decompress. Did anyone work on a similar issue?
So far I figured to get the last 1024 bytes from the file using the following code:
var fileInfo = _storage.GetObject(BucketName, fileName, new GetObjectOptions { Projection = Projection.Full });
var stream = new MemoryStream();
_storage.DownloadObject(BucketName, fileName, stream, new DownloadObjectOptions { Range = new System.Net.Http.Headers.RangeHeaderValue((long)(fileInfo.Size - 1024), (long)(fileInfo.Size)) });
The problem is I can't read the central directory from this stream:
ZipArchive z = new ZipArchive(stream);
You can try to adapt sunzip to your needs. It reads a zip file as a stream and decompresses it.

C# MemoryStream.CopyTo(fileStream) adding extra bytes and corrupting OpenXML file

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.

System.IO: Convert zip file to byte[] without saving file

I'm having trouble using the System.IO.Compression library to create a zip file and then return the byte[] for that zip file. So I want the method to take an input string--the path for the file to be zipped--and return the corresponding byte array for the file once it's been zipped. Currently, I can save the zip file to some known path and then use System.IO.File.ReadAllBytes(path) to get the byte[].
But how can I do this if I don't know beforehand where that zip file should end up (if anywhere at all)? My attempt at the solution is below. It doesn't seem to be working because when I try to reconstruct the zip file from the byte array it says that the archive is in an unknown format or damaged. Note: I've been able to do this using the Ionic DotNetZip library; however, I'm trying to do the same with only the use of System.IO libraries.
Any ideas on how to go about this?
private static byte[] CreateZipAndFindBytes(string importfile)
{
byte[] retVal = null;
using (System.IO.MemoryStream memStream = new System.IO.MemoryStream())
{
//ZipArchive(Stream, ZipArchiveMode, Boolean) //true means leave stream open
using (System.IO.Compression.ZipArchive archive = new System.IO.Compression.ZipArchive(memStream, System.IO.Compression.ZipArchiveMode.Create, true))
{
archive.CreateEntryFromFile(importfile, "test.zip"); //adds the file to the zip
}
retVal = memStream.ToArray();
}
return retVal;
}
entryName, the second parameter of CreateEntryFromFile is supposed to be "The name of the entry to create in the zip archive." It's not the name of your zip file.
you're zipping the import file, but also renaming it with a .zip extension, and I'm assuming that the import file is not actually a zip.
do this instead
archive.CreateEntryFromFile(importfile, importfile); //adds the file to the zip

Download from byte array from CRM

In Microsoft CRM we have an attachment that should be fetched and downloaded. So I have a byte array that represents the fetched file:
byte[] fileContent = Convert.FromBase64String(query.DocumentBody);
If I use this code, of course it can be downloaded but the file path should be hardcoded (like C:/<folder name>/) and I don't want it like that.
using (FileStream fileStream = new FileStream(path + query.FileName, FileMode.OpenOrCreate))
{
byte[] fileContent = Convert.FromBase64String(query.DocumentBody);
fileStream.Write(fileContent, 0, fileContent.Length);
//Response.OutputStream.WriteByte(fileContent);
}
How can I download the file from a byte array? I've tried searching for ways but it all needs a file path, and I can't provide that file path since the object is a byte array.
I'm not sure what exactly is your problem, but following should write byte array to output stream. You may need "content-disposition" header for file name and "content-type" to let browser offer "download" instead of trying to open directly:
Response.OutputStream..Write(fileContent , 0, fileContent .Length);

GZipStream not working

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/

Categories

Resources