New to this, i have been using serializing for sending image data constantly.
Meaning i save an image in a memorystream, and send it with serializing.
But i don´t understand if i am supposed to use Serializing or Networkstream.
The only thing i am doing is sending images in a loop.
I have been trying to get a Networkstream to work, but it doesn´t go my way, so here is my code. It sens only 1 image for some reason, when i use serializing it goes constantly.
private void Initialize()
{
NetSerializer.Serializer.Initialize(new Type[] { typeof(MemoryStream) });
tcplisten = new System.Net.Sockets.TcpListener(IPAddress.Any, 1700);
tcplisten.Start();
}
private void Listen()
{
try
{
using (TcpClient tt1 = tcplisten.AcceptTcpClient())
{
tt1.NoDelay = true;
while (checkBox1.Checked)
{
panel1.BackgroundImage = Image.FromStream(tt1.GetStream());
}
}
}
catch
{
}
}
And here is the send:
private void Send()
{
try
{
string process = Proccessname.Text;
using (TcpClient tcp = new TcpClient())
{
tcp.NoDelay = true;
while (capcon == true)
{
if (tcp.Connected)
{
PrintWindow(process, tcp.GetStream());
}
else
{
tcp.Connect(adress);
}
}
}
}
catch
{
}
}
what PrintWindow(process, tcp.GetStream()); does is simply, screenshot an a window (process = the name of the process), then where to save it (tcp.GetStream()) which makes it send and stream it (at least i think so).
It does work a bit, if i disconnect and connect etc, it will send an image, but it´s pretty random i think, not sure why i have to do that, i want it to keep sending as long as the loop is active.
(This works perfectly with Serialization and MemoryStream (save the bitmap in a memorystream and use it in Serialization) ).
So this is like 2 questions, depending on the answer of the first.
What is faster for my purpose, (Sending an image), Serialization or NetworkStream?
If NetworkStream is faster, how can i get it to work, what´s wrong with my code above.
Thanks
if (tcp.Connected)
{
MemoryStream ms = new MemoryStream();
PrintWindow(process, ms);
panel1.BackgroundImage = Image.FromStream(ms);
}
This works, so it is getting connected, and the data is getting saved (changed to MemoryStream instead of NetworkStream for this).
You say you get an image when you disconnect. That makes sense because you're reading the image directly from the stream. Since the stream is a NetworkStream, it has no idea when it has to end.
My suggestion is to add the size of the image buffer before sending the actual image buffer when receiving/sending it using a BinaryReader, like this:
BinaryReader reader = new BinaryReader(netStream);
while (true) {
// read how big the image buffer is
int ctBytes = reader.ReadInt32();
// read the image buffer into a MemoryStream
MemoryStream ms = new MemoryStream(reader.ReadBytes(ctBytes));
// get the image from the MemoryStream
Image img = Image.FromStream(ms);
// ...do something with img
}
And when sending:
BinaryWriter writer = new BinaryWriter(netStream);
while (someCondition) {
// get the image
Image img = SomeImage();
// save the image to a MemoryStream
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
// get the image buffer
byte[] buffer = new byte[ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(buffer, 0, buffer.Length);
// write the size of the image buffer
writer.Write(buffer.Length);
// write the actual buffer
writer.Write(buffer);
}
I tweaked the code a bit, so just gonna write it here for other people.
Now sure if it´s better or worse though.
This is the sending part, i skip to convert to a byte[] and just use the MemoryStream.
using (MemoryStream ms = PrintWindow(process))
{
writer.Write(ms.Length);
writer.Write(ms.GetBuffer());
}
And here for the receiving part:
panel1.BackgroundImage = Image.FromStream(new MemoryStream(reader.ReadBytes((Int32)reader.ReadInt64())));
Put everything in one command.
Of course the BinaryWriter/Reader are there, but i didn´t put them here, as i am just showing how i changed the code.
I used Angelo Geels Code above*
EDIT: for some reason it only seems to work in .bmp, not sure why, doesn´t make sense to me.
EDIT 2:
This can be solved with:
writer.Write(ms.GetBuffer(),0,(int)ms.Length);
Think that´s the way.
Related
I have the following code to send a picture to a receiving application
public static void sendFile(string file, string ip)
{
using (TcpClient client = new TcpClient())
{
client.Connect(IPAddress.Parse(ip), 44451);
//Console.WriteLine(ip);
NetworkStream nwStream = client.GetStream();
MemoryStream ms = new MemoryStream();
Image x = Image.FromFile(file);
x.Save(ms, x.RawFormat);
byte[] bytesToSend = ms.ToArray();
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
nwStream.Flush();
client.Close();
}
}
and I'm receiving the file on the other end with this
NetworkStream nwStream = clientCopy.GetStream();
byte[] buffer = new byte[clientCopy.ReceiveBufferSize];
//---read incoming stream---
int bytesRead = nwStream.Read(buffer, 0, clientCopy.ReceiveBufferSize);
MemoryStream ms = new MemoryStream(buffer);
Image returnImage = Image.FromStream(ms);
//ms.Flush();
//ms.Close();
String path;
if (!Directory.Exists(path = #"C:\Users\acer\AppData\Roaming\test"))
{
Directory.CreateDirectory(#"C:\Users\acer\AppData\Roaming\test");
}
string format;
if (ImageFormat.Jpeg.Equals(returnImage.RawFormat))
{
format = ".jpg";
}
else if (ImageFormat.Png.Equals(returnImage.RawFormat))
{
format = ".png";
}
else
{
format = ".jpg";
}
returnImage.Save(#"C:\Users\acer\AppData\Roaming\test\default_pic" + format, returnImage.RawFormat);
If i'm sending a picture that is small (around <20kb) the file is received 100% on the other end but if I send a file around >=100kb, the picture is received but only half of the image is loaded. I'm aware of the approach of reading the stream until all data is read but I don't know how to implement it right.
Thank you
You're only calling Read once, which certainly isn't guaranteed to read all the bytes. You could either loop, calling Read and copying the relevant number of bytes on each iteration, or you could use Stream.CopyTo:
var imageStream = new MemoryStream();
nwStream.CopyTo(imageStream);
// Rewind so that anything reading the data will read from the start
imageStream.Position = 0;
... or you could just read the image straight from the network stream:
// No need for another stream...
Image returnImage = Image.FromStream(nwStream);
(It's possible that would fail due to the stream being non-seekable... in which case using CopyTo as above would be the simplest option.)
The TCP protocol (like any other stream protocol) can't be used to transfer data as is. Most of the time it is impossible to know whether all data is arrived or whether it is received unrelated chunk of data together with the expected one. Therefore it is almost always needed to define underlying protocol, for example by sending a message header (like in HTTP) or defining a message separator (like line break in Telnet; however, using separators for big size messages are impractical). In most simple case it is enough to define very simple header that contains only the length of the message
Thus, in your case you can send 4 byte image length and then the image. On the server side you will read the 4 bytes size and then in the loop call the Read until complete message is recieved.
Please note that you can receive more bytes than expected. It means that the last chunk contains the beginning of the next message.
I have a function that returns an array of bytes containing the data of a bmp img live from a camera (including header).
I write that array in to a MemoryStream object.
That object, I pass to a Image object constructor, which will be passed to a PictureBox.
tl;dr:
byte[] AoB = GetImage();
MemoryStream ms = new MemoryStream();
ms.Write(AoB, 0, AoB.Length);
pictureBoxImage.Image = Image.FromStream(ms);
ms.Dispose();
All of this is done in a timer with a delay of 200 ms (5fps).
It works fine for about a minute or 2 until OutOfMemory exception.
For some reason, even though I dispose of the memory used, it keeps generating new one.
I've also tried to declare ms as global and flush it every time, still no go.
How can I stream the images while using the same memory space?
Try disposing the Image objects when you're done with them as well:
byte[] AoB = GetImage();
using (MemoryStream ms = new MemoryStream()) {
ms.Write(AoB, 0, AoB.Length);
Image old = pictureBoxImage.Image;
pictureBoxImage.Image = Image.FromStream(ms);
if (old != null) {
old.Dispose();
}
}
You definitely should dispose the old images when you are done with them (as adv12 mentioned), however you are also creating two byte[]s in memory. The first one is the one you get from GetImage() the second one is the one stored inside the MemoryStream and that one could be larger than your source array due to its growing algorithms.
Instead use this overload of the MemoryStream constructor to allow you to pass the byte[] directly in and the MemoryStream will reuse that single array for its internal store, reducing the memory requirement.
byte[] AoB = GetImage();
using (MemoryStream ms = new MemoryStream(AoB)) {
Image old = pictureBoxImage.Image;
pictureBoxImage.Image = Image.FromStream(ms);
old.Dispose();
}
Try setting your timer's AutoReset=false and manually starting it over at the end of the last call.
myTimer.AutoReset = false;
Start after image assignment.
byte[] AoB = GetImage();
MemoryStream ms = new MemoryStream();
ms.Write(AoB, 0, AoB.Length);
pictureBoxImage.Image = Image.FromStream(ms);
ms.Dispose();
myTimer.Start().
The timer has the potential to get ahead of the retrieval of the images.
I am trying to send large objects (>30MB) to a MSMQ queue. Due to the large amount of data we are are tring to send the idea was to GZip the objects prior to sending them, then unzipping them on the receiving end.
However, writing the compressed stream to the message.BodyStream property seems to work, but not reading it out from there.
I don't know what's wrong.
Message l_QueueMessage = new Message();
l_QueueMessage.Priority = priority;
using (MessageQueue l_Queue = CreateQueue())
{
GZipStream stream = new GZipStream(l_QueueMessage.BodyStream, CompressionMode.Compress);
Formatter.Serialize(stream, message);
l_Queue.Send(l_QueueMessage);
}
The Formatter is a global property of type BinaryFormatter. This is used to serialize/deserialize to the type of object we want to send/receive, e.g. "ProductItem".
The receving end looks like this:
GZipStream stream = new GZipStream(l_Message.BodyStream, CompressionMode.Decompress);
object decompressedObject = Formatter.Deserialize(stream);
ProductItem l_Item = decompressedObject as ProductItem;
m_ProductReceived(sender, new MessageReceivedEventArgs<ProductItem>(l_Item));
l_ProductQueue.BeginReceive();
I get an EndOfStreamException "{"Unable to read beyond the end of the stream."} trying to deserialize
at System.IO.BinaryReader.ReadByte()
Using the messageBodyStream property I actually circumvent the message.Formatter, which I don't initialize to anything, becasue I'm using my own ser/deser mechanism with the GZipStream. However, I am not sure if that's the correct way of doing this.
What am I missing?
Thanks!
In your original code, the problem is that you need to close the GZipStream in order for a GZip footer to be written correctly, and only then you can send it. If you dont, you end up sending bytes that can not be deserialized. That's also why your new code where sending is done later works.
OK, I made this work. The key was to convert the decompressed stream on the receiver to a byte[] array. Then the deserialization started working.
The sender code (notice the stream is closed before sending the message):
using (MessageQueue l_Queue = CreateQueue())
{
using (GZipStream stream = new GZipStream(l_QueueMessage.BodyStream, CompressionMode.Compress, true))
{
Formatter.Serialize(stream, message);
}
l_Queue.Send(l_QueueMessage);
}
The receiving end (notice how I convert the stream to a byte[] then deserialize):
using (GZipStream stream = new GZipStream(l_QueueMessage.BodyStream, CompressionMode.Decompress))
{
byte[] bytes = ReadFully(stream);
using (MemoryStream ms = new MemoryStream(bytes))
{
decompressedObject = Formatter.Deserialize(ms);
}
}
Still, don't know why this works using the ReadFully() function and not the Stream.CopyTo().
Does anyone?
Btw, ReadFully() is a function that creates a byte[] out of a Stream. I have to credit Jon Skeet for this at http://www.yoda.arachsys.com/csharp/readbinary.html. Thanks!
Try to separate compressing and sending:
byte[] binaryBuffer = null;
using (MemoryStream compressedBody = new MemoryStream())
{
using(GZipStream stream = new GZipStream(compressedBody, CompressionMode.Compress))
{
Formatter.Serialize(compressedBody, message);
binaryBuffer = compressedBody.GetBuffer();
}
}
using (MessageQueue l_Queue = CreateQueue())
{
l_QueueMessage.BodyStream.Write(binaryBuffer, 0, binaryBuffer.Length);
l_QueueMessage.BodyStream.Seek(0, SeekOrigin.Begin);
l_Queue.Send(l_QueueMessage);
}
Here is what I am trying to do: Thread B will download some images and store those images in a shared resource: Static ArrayList IMBuffer; thread A will take an image from IMBuffer and do something with it. The following is what I got:
Thread B:
// do something
System.Net.WebClient myWebClient = new System.Net.WebClient();
try
{ myWebClient.DownloadFile(pth, "BufferImg"); }
catch
{ // some stuff }
// add new dled image to IMBuffer
fs = new FileStream("BufferImg", FileMode.Open, FileAccess.Read);
Image img = Image.FromStream(fs);
lock (IMBuffer)
{ IMBuffer.Add(img); }
img.Dispose();
lock (IMRequest) { IMRequest.RemoveAt(0); }
myWebClient.Dispose();
//fs.Dispose();
// File.Delete("BufferImg");
// do something else
Thread A:
// do something
Image nextImg;
lock (IMBuffer)
{
nextImg = (Image)IMBuffer[0];
nextImg.Save(DLedIM);
}
// do something else
and here is the problem I am running to; since the images in IMBuffer was opened using a filestream, when the stream is disposed, the line: nextImg.Save(DLedIM); is causing "file is been used by another process" error. However if fs.Dispose(); line is commented out, then the program is locking up "BufferImg", as the result, it won't be able to download image to "BufferImg" after the first time. What should I do to fix this problem? Or is there a simpler way to accomplish what I am trying to do?
This should work:
byte[] buffer;
using (FileStream fs = new FileStream("BufferImg", FileMode.Open, FileAccess.Read))
{
buffer = new byte[fs.Length];
fs.Read(buffer, 0, (int)fs.Length);
}
using(Image img = Image.FromStream(new MemoryStream(buffer))
{
//...
}
Using a MemoryStream you avoid having to hold on to the FileStream - at this point the image does not have any connection at all to the file and hence you shouldn't have any problem with file locking.
Instead of implementing your own multithread producer/consumer workflow (and then having to debug it), why not just use an existing threadsafe (nay, concurrent) queue provided by .NET? Could save you a lot of trial and even more errors.
Details here: Thread-safe blocking queue implementation on .NET
i've got some binary data which i want to save as an image. When i try to save the image, it throws an exception if the memory stream used to create the image, was closed before the save. The reason i do this is because i'm dynamically creating images and as such .. i need to use a memory stream.
this is the code:
[TestMethod]
public void TestMethod1()
{
// Grab the binary data.
byte[] data = File.ReadAllBytes("Chick.jpg");
// Read in the data but do not close, before using the stream.
Stream originalBinaryDataStream = new MemoryStream(data);
Bitmap image = new Bitmap(originalBinaryDataStream);
image.Save(#"c:\test.jpg");
originalBinaryDataStream.Dispose();
// Now lets use a nice dispose, etc...
Bitmap2 image2;
using (Stream originalBinaryDataStream2 = new MemoryStream(data))
{
image2 = new Bitmap(originalBinaryDataStream2);
}
image2.Save(#"C:\temp\pewpew.jpg"); // This throws the GDI+ exception.
}
Does anyone have any suggestions to how i could save an image with the stream closed? I cannot rely on the developers to remember to close the stream after the image is saved. In fact, the developer would have NO IDEA that the image was generated using a memory stream (because it happens in some other code, elsewhere).
I'm really confused :(
As it's a MemoryStream, you really don't need to close the stream - nothing bad will happen if you don't, although obviously it's good practice to dispose anything that's disposable anyway. (See this question for more on this.)
However, you should be disposing the Bitmap - and that will close the stream for you. Basically once you give the Bitmap constructor a stream, it "owns" the stream and you shouldn't close it. As the docs for that constructor say:
You must keep the stream open for the
lifetime of the Bitmap.
I can't find any docs promising to close the stream when you dispose the bitmap, but you should be able to verify that fairly easily.
A generic error occurred in GDI+.
May also result from incorrect save path!
Took me half a day to notice that.
So make sure that you have double checked the path to save the image as well.
Perhaps it is worth mentioning that if the C:\Temp directory does not exist, it will also throw this exception even if your stream is still existent.
Copy the Bitmap. You have to keep the stream open for the lifetime of the bitmap.
When drawing an image: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI
public static Image ToImage(this byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
using (var image = Image.FromStream(stream, false, true))
{
return new Bitmap(image);
}
}
[Test]
public void ShouldCreateImageThatCanBeSavedWithoutOpenStream()
{
var imageBytes = File.ReadAllBytes("bitmap.bmp");
var image = imageBytes.ToImage();
image.Save("output.bmp");
}
I had the same problem but actually the cause was that the application didn't have permission to save files on C. When I changed to "D:\.." the picture has been saved.
You can try to create another copy of bitmap:
using (var memoryStream = new MemoryStream())
{
// write to memory stream here
memoryStream.Position = 0;
using (var bitmap = new Bitmap(memoryStream))
{
var bitmap2 = new Bitmap(bitmap);
return bitmap2;
}
}
This error occurred to me when I was trying from Citrix. The image folder was set to C:\ in the server, for which I do not have privilege. Once the image folder was moved to a shared drive, the error was gone.
A generic error occurred in GDI+. It can occur because of image storing paths issues,I got this error because my storing path is too long, I fixed this by first storing the image in a shortest path and move it to the correct location with long path handling techniques.
I was getting this error, because the automated test I was executing, was trying to store snapshots into a folder that didn't exist. After I created the folder, the error resolved
One strange solution which made my code to work.
Open the image in paint and save it as a new file with same format(.jpg). Now try with this new file and it works. It clearly explains you that the file might be corrupted in someway.
This can help only if your code has every other bugs fixed
It has also appeared with me when I was trying to save an image into path
C:\Program Files (x86)\some_directory
and the .exe wasn't executed to run as administrator, I hope this may help someone who has same issue too.
For me the code below crashed with A generic error occurred in GDI+on the line which Saves to a MemoryStream. The code was running on a web server and I resolved it by stopping and starting the Application Pool that was running the site.
Must have been some internal error in GDI+
private static string GetThumbnailImageAsBase64String(string path)
{
if (path == null || !File.Exists(path))
{
var log = ContainerResolver.Container.GetInstance<ILog>();
log.Info($"No file was found at path: {path}");
return null;
}
var width = LibraryItemFileSettings.Instance.ThumbnailImageWidth;
using (var image = Image.FromFile(path))
{
using (var thumbnail = image.GetThumbnailImage(width, width * image.Height / image.Width, null, IntPtr.Zero))
{
using (var memoryStream = new MemoryStream())
{
thumbnail.Save(memoryStream, ImageFormat.Png); // <= crash here
var bytes = new byte[memoryStream.Length];
memoryStream.Position = 0;
memoryStream.Read(bytes, 0, bytes.Length);
return Convert.ToBase64String(bytes, 0, bytes.Length);
}
}
}
}
I came across this error when I was trying a simple image editing in a WPF app.
Setting an Image element's Source to the bitmap prevents file saving.
Even setting Source=null doesn't seem to release the file.
Now I just never use the image as the Source of Image element, so I can overwrite after editing!
EDIT
After hearing about the CacheOption property(Thanks to #Nyerguds) I found the solution:
So instead of using the Bitmap constructor I must set the Uri after setting CacheOption BitmapCacheOption.OnLoad.(Image1 below is the Wpf Image element)
Instead of
Image1.Source = new BitmapImage(new Uri(filepath));
Use:
var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filepath);
image.EndInit();
Image1.Source = image;
See this: WPF Image Caching
Try this code:
static void Main(string[] args)
{
byte[] data = null;
string fullPath = #"c:\testimage.jpg";
using (MemoryStream ms = new MemoryStream())
using (Bitmap tmp = (Bitmap)Bitmap.FromFile(fullPath))
using (Bitmap bm = new Bitmap(tmp))
{
bm.SetResolution(96, 96);
using (EncoderParameters eps = new EncoderParameters(1))
{
eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bm.Save(ms, GetEncoderInfo("image/jpeg"), eps);
}
data = ms.ToArray();
}
File.WriteAllBytes(fullPath, data);
}
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
for (int j = 0; j < encoders.Length; ++j)
{
if (String.Equals(encoders[j].MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase))
return encoders[j];
}
return null;
}
I used imageprocessor to resize images and one day I got "A generic error occurred in GDI+" exception.
After looked up a while I tried to recycle the application pool and bingo it works. So I note it here, hope it help ;)
Cheers
I was getting this error today on a server when the same code worked fine locally and on our DEV server but not on PRODUCTION. Rebooting the server resolved it.
public static byte[] SetImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
public static Bitmap SetByteToImage(byte[] blob)
{
MemoryStream mStream = new MemoryStream();
byte[] pData = blob;
mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
Bitmap bm = new Bitmap(mStream, false);
mStream.Dispose();
return bm;
}