I have a function that is returning MemoryStream array, i want to convert this memory stream array to a FileStream object.
Is it possible if yes can you please provide a way to do that...
Thanks
A.S
You cannot "convert" the stream, because a MemoryStream and a FileStream are very different things. However, you can write the entire contents of the MemoryStream to a file. There is a CopyTo method that you can use for that:
// memStream is the MemoryStream
using (var output = File.Create(filename)) {
memStream.CopyTo(output);
}
A file stream object represents an open file (from disk) as a stream. A memory stream represents an area of memory (byte array) as a stream. So you can't really convert a memory stream into a file stream directly - at least not trivially.
There are two approaches you could take:
OFFLINE: fully consume the contents of the memory stream and write it all out to a file on disk; then open that file as a file stream
ONLINE: extent the FileStream class creating an adapter that will wrap a MemoryStream object and expose it as a FileStream (essentially acting as a converter)
The reason one is marked [OFFLINE] is because you need to have to full contents of the memory stream before you output it to the file (and once you do, modifications to the file stream will not affect the memory stream; nor changes to the memory stream, such as new data, be available to the file stream)
The second one is marked as [ONLINE] because once you create the adapter and you initialize the FileStream object from the MemoryStream you could process any new data in the MemoryStream using the FileStream adapter object. You would essentially be able to read/write and seek into the memory stream using the file stream as a layer on top of the memory stream. Presumably, that's what you'd want to do..
Of course, it depends on what you need to do, but I'm leaning towards the second [ONLINE] version as the better in the general sense.
Related
Suppose you have this:
using FileStream fileStream = ...;
fileStream.Write(bytes);
using GZipStream gzipStream = new GZipStream(fileStream, CompressionLevel.Optimal);
gzipStream.Write(bytesToCompress);
Would writing to fileStream first, and then creating a new GZipStream from that stream and then writing bytes to it work?
Do I have to flush fileStream first? Or is this simply unsafe?
Would writing to fileStream first, and then creating a new GZipStream from that stream and then writing bytes to it work?
Yes, that is sort of the point of streams really, that they are chainable. See Compose streams.
Do I have to flush fileStream first? Or is this simply unsafe?
Flush is only necessary when you wish to purge buffers and guarantee that the data are written to the backing store (e.g. the filesystem or network).
I have a ZipArchive object which contains an XML file that I am modifying. I then want to return the modified ZipArchive.
Here's the code I have:
var package = File.ReadAllBytes(/* location of existing .zip */);
using (var packageStream = new MemoryStream(package, true))
using (var zipPackage = new ZipArchive(packageStream, ZipArchiveMode.Update))
{
// obtain the specific entry
var myEntry = zipPackage.Entries.FirstOrDefault(entry => /* code elided */));
XElement xContents;
using (var reader = new StreamReader(myEntry.Open()))
{
// read the contents of the myEntry XML file
// then modify the contents into xContents
}
using (var writer = new StreamWriter(myEntry.Open()))
{
writer.Write(xContents.ToString());
}
return packageStream.ToArray();
}
This code throws a "Memory stream is not expandable" exception on the packageStream.ToArray() call.
Can anyone explain what I've done wrongly, and what is the correct way of updating an existing file inside a ZipArchive?
Clearly, ZipArchive wants to expand or resize the ZIP archive stream. However, you have provided a MemoryStream with a fixed stream length (due to using the constructor MemoryStream(byte[], bool), which creates a memory stream with a fixed length that is equal to the length of the array provided to the constructor).
Since ZipArchive wants to expand (or resize) the stream, provide an resizable MemoryStream (using its parameter-less constructor). Then copy the original file data into this MemoryStream and proceed with the ZIP archive manipulations.
And don't forget to reset the MemoryStream read/write position back to 0 after copying the original file data into it, otherwise ZipArchive will only see "End of Stream" when trying to read the ZIP archive data from this stream.
using (var packageStream = new MemoryStream())
{
using (var fs = File.OpenRead(/* location of existing .zip */))
{
fs.CopyTo(packageStream);
}
packageStream.Position = 0;
using (var zipPackage = new ZipArchive(packageStream, ZipArchiveMode.Update))
{
... do your thing ...
}
return packageStream.ToArray();
}
This code here contains one more correction. In the original code in the question, return packageStream.ToArray(); has been placed within the using block of the ZipArchive. At the time this line will be executed, the ZipArchive instance might not yet have written all data to the MemoryStream, perhaps keeping some data still in some internal buffers and/or perhaps having deferred writing some ZIP data structures.
To ensure that the ZipArchive has actually written all necessary data completely to the MemoryStream, it is here sufficient to move return packageStream.ToArray(); outside after the ZipArchive using block. At the end of its using block, the ZipArchive will be disposed which will also ensure that ZipArchive has written all so far yet unwritten data to the stream. Thus, accessing the MemoryStream after the ZipArchive has been disposed off will yield the complete data of the completely updated ZIP archive.
Side note: Do this only with small-ish ZIP files. The MemoryStream will obviously use internal data buffers (arrays) to hold the data in the MemoryStream. However, packageStream.ToArray(); will create a copy of the data in the MemoryStream, so for a period of time the memory requirements of this routine will be a little more than twice the size of the ZIP archive.
When I use zlib in C/C++, I have a simple method uncompress which only requires two buffers and no more else. Its definition is like this:
int uncompress (Bytef *dest, uLongf *destLen, const Bytef *source,
uLong sourceLen);
/*
Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry,
destLen is the total size of the destination buffer, which must be
large enough to hold the entire uncompressed data. (The size of
the uncompressed data must have been saved previously by the
compressor and transmitted to the decompressor by some mechanism
outside the scope of this compression library.) Upon exit, destLen
is the actual size of the uncompressed data.
uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output
buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
In the case where there is not enough room, uncompress() will fill
the output buffer with the uncompressed data up to that point.
*/
I want to know if C# has a similar way. I checked SharpZipLib FAQ as follows but did not quite understand:
How do I compress/decompress files in memory?
Use a memory stream when creating the Zip stream!
MemoryStream outputMemStream = new MemoryStream();
using (ZipOutputStream zipOutput = new ZipOutputStream(outputMemStream)) {
// Use zipOutput stream as normal
...
You can get the resulting data with memory stream methods ToArray or GetBuffer.
ToArray is the cleaner and easiest to use correctly with the penalty
of duplicating allocated memory. GetBuffer returns a raw buffer raw
and so you need to account for the true length yourself.
See the framework class library help for more information.
I can't figure out if this block of code is for compression or decompression, if outputMemStream meas a compressed stream or an uncompressed stream. I really hope there is a easy-to-understand-way like in zlib. Thanks you very much if you can help me.
Check out the ZipArchive class, which I think has the features you need to accomplish in-memory decompression of zip files.
Assuming you have an array of bytes (byte []) which represent the ZIP file in memory, you have to instantiate a ZipArchive object which will be used to read that array of bytes and interpret them as the ZIP file you whish to load. If you check the ZipArchive class' available constructors in documentation, you will see that they require a stream object from which the data will be read. So, first step would be to convert your byte [] array to a stream that can be read by the constructors, and you can do this by using a MemoryStream object.
Here's an example of how to list all entries inside of a ZIP archive represented in memory as a bytes array:
byte [] zipArchiveBytes = ...; // Read the ZIP file in memory as an array of bytes
using (var inputStream = new MemoryStream(zipArchiveBytes))
using (var zipArchive = new ZipArchive(inputStream, ZipArchiveMode.Read))
{
Console.WriteLine("Listing archive entries...");
foreach (var archiveEntry in zipArchive.Entries)
Console.WriteLine($" {archiveEntry.FullName}");
}
Each file in the ZIP archive will be represented as a ZipArchiveEntry instance. This class offers properties which allow you to retrieve information such as the original length of a file from the ZIP archive, its compressed length, its name, etc.
In order to read a specific file which is contained inside the ZIP file, you can use ZipArchiveEntry.Open(). The following exemplifies how to open a specific file from an archive, if you have its FullName inside the ZIP archive:
ZipArchiveEntry archEntry = zipArchive.GetEntry("my-folder-inside-zip/dog-picture.jpg");
byte[] readResult;
using (Stream entryReadStream = archEntry.Open())
{
using (var tempMemStream = new MemoryStream())
{
entryReadStream.CopyTo(tempMemStream);
readResult = tempMemStream.ToArray();
}
}
This example reads the given file contents, and returns them as an array of bytes (stored in the byte[] readResult variable) which you can then use according to your needs.
I'm attempting to take a large file, uploaded from a web app, and make it a memorystream for processing later. I was receiving OutOfMemory exceptions when trying to copy the HttpPostedFileBase's inputstream into a new MemoryStream. During troubleshooting, I tried just creating a new MemoryStream and allocate the same amount of space (roughly) as the length of the InputStream (935,638,275), like so:
MemoryStream memStream = new MemoryStream(935700000);
Even doing this results in a System.OutOfMemoryException on this line.
I only slightly understand MemoryStreams, and this seems to be something to do with how MemoryStreams buffer data. Is there a way for me to get all of the data into one MemoryStream without too much fuss?
I am not sure what the processing involves, but the HttpPostedFileBase already contains a stream with the data. You can use that stream to process what you need to do.
If you really need to move back and forth or multiple times over the stream, and the input stream does not support seeking/positioning, you may want to stream the data to a temporary local file first and then use a file stream to do your processing against that file.
If many people uploading via your web app, the array size you specified would quickly eat up all memory using a MemoryStream.
So I have some code that takes a capture of the screen and saves it to a jpeg file. This works fine, however I want to instead save the jpeg encoded capture to a new ZipArchive without writing the Bitmap to the file system first.
Here is what I have so far:
FileInfo zipArchive = new FileInfo(fileToZip.FullName + ".zip");
using (ZipArchive zipFile = ZipFile.Open(zipArchive.FullName, ZipArchiveMode.Create)))
{
ZipArchiveEntry zae = zipFile.CreateEntry(fileToZip.FullName, CompressionLevel.Optimal);
using (Stream zipStream = zae.Open())
bmp.Save(zipStream, ImageFormat.Jpeg);
}
The problem is that on the bmp.Save() line a System.NotSupportedException is thrown
This stream from ZipArchiveEntry does not support seeking.
I've seen a lot of examples that write directly to the Stream returned from zae.Open() so I am not sure why this doesn't work because I figured that all bmp.Save() would need to do is write, not seek. I don't know if this would work but I don't want to have to save the Bitmap to a MemoryStream and the copy that stream to the Stream returned from zae.Open() because it feels like unnecessary extra work. Am I missing something obvious?
Many file formats have pointers to other parts of the file, or length values, which may not be known beforehand. The simplest way is to just write zeros first, then the data and then seek to change the value. If this way is used, there is no way to get by this, so you will need to first write the data into a MemoryStream and then write the resulting data into the ZipStream, as you mentioned.
This doesn't really add that much code and is a simple fix for the problem.