How to output Metafile (emf) into stream (or byte[]) - c#

For an application I'm currently developing, I need to create a .emf file.
I've got it working when I output the result directly to a file, but I can't get it to output into a stream, which is essential to what I'm trying to do.
This code correctly generates the file, but it's outputted directly to the harddisk.
var sizedImage = new Bitmap(103, 67);
using(var graphicsFromSizedImage = Graphics.FromImage(sizedImage))
using(var metafile = new Metafile("result.emf", graphicsFromSizedImage.GetHdc()))
using(var graphics = Graphics.FromImage(metafile))
{
graphics.DrawStuff()
graphicsFromSizedImage.ReleaseHdc();
}
Here's my attempt to output it to a memorystream, so I could get a byte[] from that stream:
byte[] resultingBytes;
var sizedImage = new Bitmap(103, 67);
using(var stream = new MemoryStream())
using(var graphicsFromSizedImage = Graphics.FromImage(sizedImage))
using(var metafile = new Metafile(stream, graphicsFromSizedImage.GetHdc()))
using(var graphics = Graphics.FromImage(metafile))
{
graphics.DrawStuff()
graphicsFromSizedImage.ReleaseHdc();
resultingBytes = stream.GetBuffer();
}
File.WriteAllBytes("result.emf", resultingBytes);
But all this does is create an empty file. When I run through it with the debugger, I can see the stream remain empty.
What am I missing here..?

I found the answer thanks to #Selvin
It turns out, the changes are only written to the MemoryStream when the "graphics" object is disposed. So, just by adding an extra set of brances, the problem is resolved.
Here's my working code:
byte[] resultingBytes;
var sizedImage = new Bitmap(103, 67);
using(var stream = new MemoryStream())
using(var graphicsFromSizedImage = Graphics.FromImage(sizedImage))
using(var metafile = new Metafile(stream, graphicsFromSizedImage.GetHdc()))
{
using(var graphics = Graphics.FromImage(metafile))
{
graphics.DrawStuff()
graphicsFromSizedImage.ReleaseHdc();
}
resultingBytes = stream.ToArray();
}
File.WriteAllBytes("result.emf", resultingBytes);
Edit: As some have pointed out, stream.GetBuffer() will return the entire buffer. I changed it to stream.ToArray(), which should be better.

Related

MemoryStream into MagicImage

I am trying to store MemoryStream into MagicImage, but when I am trying to upload file with heic format it still uploading with heic format, but it should upload it with jpeg format. So I kind of do not understand where I am doing wrong. So could someone help me? I am trying it open in Frame in web, but it does not open bc it is not converting it to jpeg.
using (MemoryStream ms = new MemoryStream())
{
create.PostedFile.InputStream.CopyTo(ms);
var data = ms.ToArray();
byte[] data1 = null;
using (var image = new MagickImage(data))
{
// Sets the output format to jpeg
image.Format = MagickFormat.Jpeg;
// Create byte array that contains a jpeg file
data1 = image.ToByteArray();
}
var file = new ClientFile
{
Data = data1, // here where it should store it in jpeg
};
While I have never used your way of writing the image, this is what I use in my implementations and it always works:
var image = new MagickImage(sourceStream);
var format = MagickFormat.Jpg;
var stream = new MemoryStream();
image.Write(stream, format);
stream.Position = 0;
EDIT
If you don't add:
stream.Position = 0
sending the stream will not work as it will start saving from the current position which is at the end of the stream.

Memory leak with a memory stream

I know this code is far from perfect but in my case this was the only
way to do it correctly because im embedding WPF in C#, and when
applying text regulary the Spellcheck does not work correctly
So this is my code:
RichTextBox temphotfix = new RichTextBox();
temphotfix.Font = new Font(temphotfix.Font.Name, 14);
System.Windows.Documents.TextRange range = new System.Windows.Documents.TextRange(omschrijving.Document.ContentStart, omschrijving.Document.ContentEnd);
temphotfix.Text = oms;
string temp = temphotfix.Rtf;
byte[] byteArray = Encoding.ASCII.GetBytes(temp);
MemoryStream stream = new MemoryStream(byteArray);
range.Load(stream, DataFormats.Rtf);
range = null;
temp = null;
byteArray = null;
temphotfix.Dispose();
stream.Dispose();
I stress tested this, and it seems like ever about 5 times the script gets ran, it adds about 1 MB ram.
What am i doing wrong, i litterly made everyting i used null, or desposed them.
As I told above in comment you can using, you can try this code. hope this should help.
using (RichTextBox temphotfix = new RichTextBox())
{
temphotfix.Font = new Font(temphotfix.Font.Name, 14);
System.Windows.Documents.TextRange range = new System.Windows.Documents.TextRange(omschrijving.Document.ContentStart, omschrijving.Document.ContentEnd);
temphotfix.Text = oms;
string temp = temphotfix.Rtf;
byte[] byteArray = Encoding.ASCII.GetBytes(temp);
using (MemoryStream stream = new MemoryStream(byteArray))
{
range.Load(stream, DataFormats.Rtf);
}
range = null;
temp = null;
byteArray = null;
//temphotfix.Dispose();
//stream.Dispose();
}

ZipArchive Created with System.IO.Compression is Damaged

I am having a hard time creating a ZipArchive successfully on Asp.net core MVC. I have an excel file generated with data that works and I need to put in an archive. This is what I've done so far
public FileResult ExportGoodsReceiptData()
{
var records = _salesService.GetAllReceipts();
var lineRecords = _salesService.GetAllReceiptLines();
var result = _salesService.ExportGoodsReceiptData(records);
var lineResult = _salesService.ExportGoodsReceiptLineData(lineRecords);
byte[] resultArr = StreamToByteArray(result);
byte[] lineResultArr = StreamToByteArray(lineResult);
using(MemoryStream stream = new MemoryStream())
{
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
{
var zipArchiveEntry = archive.CreateEntry("GoodsReceipts.csv", CompressionLevel.Fastest);
using (var zipStream = zipArchiveEntry.Open())
using (var resultCom = new MemoryStream(resultArr))
{
resultCom.CopyTo(zipStream);
}
}
return new FileStreamResult(stream, "application/zip") { FileDownloadName = "GoodsReceiptsArchive.zip" };
}
}
When I run it, I get the zipfile, but can't open it. It throws error stating that it may have been damaged. I debugged the code to notice that one of the properties (length property) throws an invalidOperation exception. My approach looks identical to most samples I found online. Don't know how else to solve this. Please help.
Your problem is that you're disposing of your memory stream before you return it. Remove this using:
using(MemoryStream stream = new MemoryStream())
Replace it with:
var stream = new MemoryStream();
Asp.Net MVC will automatically dispose of the stream for you.

Save a zip file to memory and unzip file from stream and get content

I am currently working on integrating Amazon Prime on our system and being stuck at getting the label back as ZPL format.
Basically, Amazon returns a base64 string, we will need to convert that string to a byte array, then save that array as a *.gzip file. From that gzip file, we can extract the content and get the zpl label content.
My question is, how we can do all of above without storing any temp files to system. I have researched some solutions but none is working for me.
My current code as below:
var str = "base64string";
var label = Convert.FromBase64String(str);
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var demoFile = archive.CreateEntry("label.zip");
var entryStream = demoFile.Open();
using (var bw = new BinaryWriter(entryStream))
{
bw.Write(label);
}
var data = new MemoryStream();
using (var zip = ZipFile.Read(entryStream))
{
zip["label"].Extract(data);
}
data.Seek(0, SeekOrigin.Begin);
entryStream.Close();
}
using (var fileStream = new FileStream(#"D:\test.zip", FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
If I save the file as test.zip, I can successfully get the label back. But if I try to extract it directly to another stream, I get an error
A stream from ZipArchiveEntry has been disposed
I've done something similar, taking PNG label data from a zipped web response. This is how I went about that
using (WebClient webClient = new WebClient())
{
// Download. Expect this to be a zip file
byte[] data = webClient.DownloadData(urlString);
MemoryStream memoryStream = new MemoryStream(data);
ZipArchive zipArchive = new ZipArchive(memoryStream);
foreach (var zipEntry in zipArchive.Entries)
{
// Can check file name here and ignore anything in zip we're not expecting
if (!zipEntry.Name.EndsWith(".png")) continue;
// Open zip entry as stream
Stream extractedFile = zipEntry.Open();
// Convert stream to memory stream
MemoryStream extractedMemoryStream = new MemoryStream();
extractedFile.CopyTo(extractedMemoryStream);
// At this point the extractedMemoryStream is a sequence of bytes containing image data.
// In this test project I'm pushing that into a bitmap image, just to see something on screen, but could as easily be written to a file or passed for storage to sql or whatever.
BitmapDecoder decoder = PngBitmapDecoder.Create(extractedMemoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapFrame frame = decoder.Frames.First();
frame.Freeze();
this.LabelImage.Source = frame;
}
}
I was overthinking it. I finally found a simple way to do it. We just need to convert that base64 string to bytes array and use GzipStream to directly decompress it. I leave the solution here in case someone needs it. Thanks!
var label = Convert.FromBase64String(str);
using (var compressedStream = new MemoryStream(label))
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
using (var resultStream = new MemoryStream())
{
zipStream.CopyTo(resultStream);
return resultStream.ToArray();
}

DeflateStream doesnt work on MemoryStream?

I have the following piece of code:
MemoryStream resultStream = new MemoryStream();
string users = ""//Really long string goes here
BinaryFormatter bFormatter = new BinaryFormatter();
using (MemoryStream assignedUsersStream = new MemoryStream())
{
bFormatter.Serialize(assignedUsersStream, users);
assignedUsersStream.Position = 0;
using (var compressionStream =
new DeflateStream(resultStream, CompressionLevel.Optimal))
{
assignedUsersStream.CopyTo(compressionStream);
Console.WriteLine("Compressed from {0} to {1} bytes.",
assignedUsersStream.Length.ToString(),
resultStream.Length.ToString());
}
}
the thing is that resultStream is always empty!
What am I doing wrong here?
Put your verification WriteLine outside of the using. The buffers haven't been flushed yet.
using (DeflateStream compressionStream = new DeflateStream(resultStream, CompressionLevel.Optimal))
{
assignedUsersStream.CopyTo(compressionStream);
//Console.WriteLine("Compressed from {0} to {1} bytes.",
// assignedUsersStream.Length.ToString(), resultStream.Length.ToString());
}
Console.WriteLine("Compressed from {0} to {1} bytes.",
assignedUsersStream.Length, resultStream.ToArray().Length);
And aside, you don't need all those ToString()s in a writeline.
PS: All a BinaryFormatter does with a string is write the bytes with length prefix. If you don't need the prefix (my guess), it could become:
string users = "";//Really long string goes here
byte[] result;
using (MemoryStream resultStream = new MemoryStream())
{
using (DeflateStream compressionStream = new DeflateStream(resultStream,
CompressionLevel.Optimal))
{
byte[] inBuffer = Encoding.UTF8.GetBytes(users);
compressionStream.Write(inBuffer, 0, inBuffer.Length);
}
result = resultStream.ToArray();
}
The reverse is just as easy but you'll need an estimate of the maximum length to create the read-buffer:
string users2 = null;
using (MemoryStream resultStream = new MemoryStream(result))
{
using (DeflateStream compressionStream = new DeflateStream(resultStream,
CompressionMode.Decompress))
{
byte[] outBuffer = new byte[2048]; // need an estimate here
int length = compressionStream.Read(outBuffer, 0, outBuffer.Length);
users2 = Encoding.UTF8.GetString(outBuffer, 0, length);
}
}
That is because the DeflateStream doesn't flush the data to the underlying stream until it is closed. After it is closed, resultStream will contain the compressed data. Note that by default, DeflateStream closes the underlying stream when it's closed, but you don't want that, so you need to pass true for the leaveOpen parameter. Also, you don't need 2 memory streams, you can just serialize directly to the compressionStream:
string users = ""; //Really long string goes here
BinaryFormatter bFormatter = new BinaryFormatter();
using (MemoryStream resultStream = new MemoryStream())
{
using (DeflateStream compressionStream = new DeflateStream(resultStream, CompressionLevel.Optimal, true))
{
bFormatter.Serialize(compressionStream, users);
Console.WriteLine(resultStream.Length); // 0 at this point
}
Console.WriteLine(resultStream.Length); // now contains the actual length
}
From the original answer (I don't have enough credits to vote down)
Put your control WriteLine outside of the using
This is incomplete and IMO therefore misleading. DeflateStream's Dispose(bool) implementation Closes the underlying resultStream when the DeflateStream is being Finalized after it's been Garbage Collected. When this happens, resultStream.Length will throw:
Unhandled Exception: System.ObjectDisposedException: Cannot access a closed Stream.
In other words, Thomas Levesque's note is critical: also set leaveOpen to true.
An interesting question with some good points raised by HH and TL.
I've come in late to this as I ran into this same problem and reading the conflicting answers. The initial response works! as does this test (over simplified to highlight the answer):
var inStream = new MemoryStream(data);
var outStream = new MemoryStream();
using (var compressor = new DeflateStream(outStream, CompressionLevel.Optimal))
{
inStream.CopyTo(compressor);
}
return outStream;
where the using block needs to complete, triggering the compressor's Dispose, which internally Flush()es so that outStream will be guaranteed to contain the complete compressed data.

Categories