How to tell if Image.FromStream is complete - c#

i have the following code:
var request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = request.GetResponse();
var stream = response.GetResponseStream();
if (stream != null) {
Image newImage = Image.FromStream(stream, true);
pic.Thumb = newImage.ImageToByteArray();
}
What happens if the read times out? Or the connection is aborted mid-download?
The docs say that it will throw an ArgumentException if it's not a valid format or is null, but I have no idea if it will throw that exception if the image is only partially downloaded.
Unfortunately, I can't rely on the ContentLength header to tell me the proper size of the file, because the server lies and gives a larger content length than the file actually is. So my hope is that Image.FromStream will be able to tell if the image is complete or not.
Can anyone provide some insight here?
Note: ImageToByteArray is just an extension method that uses a memory stream to convert the Image to a byte[]
UPDATE:
According to Darin, then an ExternalException gets thrown when you try to save the image. However, my own testing in which I truncated an image file shows that FromStream does in fact throw an ArgumentException if the image is not the correct number of bytes.

What happens if the read times out?
An exception will be thrown before entering the if condition.
Or the connection is aborted?
An exception will be thrown before entering the if condition.
but I have no idea if it will throw that exception if the image is only partially downloaded.
An image cannot be partially downloaded. The GetResponseStream is a blocking method meaning that either you get everything, or an exception (or of course in your case you could also get an exception if what you fetched is not an image but some HTML page which will happen when you try to instantiate the Image GDI+ object).
As a side note, to avoid leaking, you probably also want to wrap this Image disposable resource into a using statement.

Related

Need help, Im trying to compress images [duplicate]

Having a code that works for ages when loading and storing images, I discovered that I have one single image that breaks this code:
const string i1Path = #"c:\my\i1.jpg";
const string i2Path = #"c:\my\i2.jpg";
var i = Image.FromFile(i1Path);
i.Save(i2Path, ImageFormat.Jpeg);
The exception is:
System.Runtime.InteropServices.ExternalException occurred
A generic error occurred in GDI+.
at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
at System.Drawing.Image.Save(String filename, ImageFormat format)
at ...
As far as I can see, there is nothing special about the image. It is approx 250 pixels in size and can be opened in e.g. Windows Image Viewer or Paint.NET:
(Since the image above, after being uploaded to Stack Overflow does not produce the error anymore, I've put the original image here)
What I discovered is that upon calling the Save method, the destination image file is being created with zero bytes.
I am really clueless on what causes the error.
My questions:
Can you think of any special thing that would hinder .NET from saving the image?
Is there any way (beside panicing) to narrow down these kind of errors?
While I still did not find out the reason what exactly caused the error when saving the image, I found a workaround to apply:
const string i1Path = #"c:\my\i1.jpg";
const string i2Path = #"c:\my\i2.jpg";
var i = Image.FromFile(i1Path);
var i2 = new Bitmap(i);
i2.Save(i2Path, ImageFormat.Jpeg);
I.e. by copying the image internally into a Bitmap instance and saving this image instead of the original image, the error disappeared.
I'm assuming that by copying it, the erroneous parts the caused the original Save call to fail are being removed an/or normalized, thus enabling the save operation to succeed.
Interestingly, the so stored image has a smaller file on disk (16 kB) than its original source (26 kB).
First of all make sure, that the desired folder has Read/Write permissions. Changing the permissions solved this problem for me.
Solution is here, you must dispose image object to release the memory on the server.
Try use using statement. Make sure destination directory on server exists too.
The reason may be that the image is loaded lazily and the loading process is not yet finished when you try to save it.
Following what's said in this blog post (assuming you're German by the picture you linked in your question) provides a possible solution. Also this SO question's accepted answer indicates this is due to the fact the image file you're trying to save to is locked.
EDIT
For Ulysses Alves, from the linked blog entry: If you load an image using Image.FromFile() it remains locked until it is disposed of. This prevents calls to Save().
pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
pictureBox1.Image.Save("C:\\test\\test2.jpg");
The above code throws an error.
To make it work, you need to copy the image. The following code works:
pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
Image copy = pictureBox1.Image;
copy.Save("C:\\test\\test2.jpg")
I found this question because I also faced the similar error and the file was actually created with zero length (if you don't see any file, first check the permissions to write into folder as other answers suggest). Although my code was slightly different (I use stream to read the image from memory, not from file), I think my answer may be helpful to anyone facing similar problem.
It may looks counter-intuitive, but you can't really dispose memory stream until you finish with image.
NOT WORKING:
Image patternImage;
using (var ms = new MemoryStream(patternBytes)) {
patternImage = new Bitmap(ms);
}
patternImage.Save(patternFile, ImageFormat.Jpeg);
Just don't dispose the stream until you done with image.
WORKS:
using (var ms = new MemoryStream(patternBytes)) {
patternImage = new Bitmap(ms);
patternImage.Save(patternFile, ImageFormat.Jpeg);
}
What is misleading:
Error message doesn't really tell you anything
You can see the image properties, like width and height, but can't
save it
my solution was to make, write temp content (File.WriteAllText) just before saving the file
Here is the code:
var i = Image.FromFile(i1Path);
File.WriteAllText(i2Path, "empty"); // <---- magic goes here
i.Save(i2Path, ImageFormat.Jpeg);
Please try and let me know
In my case I have accidentally deleted the directory where image was getting stored.
Key Information:
// Using System.Drawing.Imaging:
new Bitmap(image).Save(memoryStream, ImageFormat.Jpeg);
You MUST Cast the Image to a Bitmap to Save it.
Using:
// Using System.Drawing.Imaging:
image.Save(memoryStream, ImageFormat.Jpeg);
WILL throw the Error:
Generic GDI+ error when saving an image
Just use the visual studio as administrator or run the application created by the code as administrator it should work smoothly.
It is user access rights issue.
I faced the same and resolved it by running visual studio as administrator.
In my case, I set validateImageData to false:
Image.FromStream(stream, validateImageData: false);
solution:
Image.FromStream(stream, validateImageData: true);
Open in the program
const string i1Path = #"c:\my\i1.jpg";
const string i2Path = #"c:\my\i2.jpg";
var i = Image.FromFile(i1Path);
i.Save(i2Path, ImageFormat.Jpeg);
i.Dispose();

ReadTimeout exception with MemoryStream

The following method throws this exception
System.IO.Stream)(ms)).ReadTimeout threw an exception of type
System.InvalidOperationException'
This is the method:
private static byte[] ImageToByteArraybyMemoryStream(Bitmap bmp)
{
using (MemoryStream ms = new MemoryStream()) {
bmp.Save(ms, bmp.RawFormat);
return ms.ToArray();
}
}
however, this error doesn't occur all the time. I'll try to explain in short what happens:
I load a bitmap from a file, display it and store it in Dictionary<int,Bitmap>
When application is closed, i write bitmap to apps config-file as byte[] (and there's no exception)
On start of app i load bitmap from config-file and display it
When user changes application-data (like resizing or moving the bitmap) i rewrite the config-file the same way as i did when bitmap was loaded from file and this exception occurs.
i just googled "generic gdi+ error" others also got a problem with the bmp.Save Method. the workaround for them is to create a new Bitmap from the one you wanna save and then save this copy.
...
Bitmap copy = new Bitmap(bmp);
copy.Save(ms, copy.RawfFormat);
...
Maybe this also works for you, i can not tell you the reason for this error, its reffered to as a bug on other sites.
Other Post discussing this Problem
I faced this issue and it took a while to understand that, the object that uploaded the image did not release its located memory and lock it! and the garbage collection didn't dispose of that object, therefore the API could not stream the image.
somehow it works for the first time then it will raise the error.
Solution:
I used HttpPostFileBase to upload the image, and after it has done I set it to null
and that's it!

How to find reason for Generic GDI+ error when saving an image?

Having a code that works for ages when loading and storing images, I discovered that I have one single image that breaks this code:
const string i1Path = #"c:\my\i1.jpg";
const string i2Path = #"c:\my\i2.jpg";
var i = Image.FromFile(i1Path);
i.Save(i2Path, ImageFormat.Jpeg);
The exception is:
System.Runtime.InteropServices.ExternalException occurred
A generic error occurred in GDI+.
at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
at System.Drawing.Image.Save(String filename, ImageFormat format)
at ...
As far as I can see, there is nothing special about the image. It is approx 250 pixels in size and can be opened in e.g. Windows Image Viewer or Paint.NET:
(Since the image above, after being uploaded to Stack Overflow does not produce the error anymore, I've put the original image here)
What I discovered is that upon calling the Save method, the destination image file is being created with zero bytes.
I am really clueless on what causes the error.
My questions:
Can you think of any special thing that would hinder .NET from saving the image?
Is there any way (beside panicing) to narrow down these kind of errors?
While I still did not find out the reason what exactly caused the error when saving the image, I found a workaround to apply:
const string i1Path = #"c:\my\i1.jpg";
const string i2Path = #"c:\my\i2.jpg";
var i = Image.FromFile(i1Path);
var i2 = new Bitmap(i);
i2.Save(i2Path, ImageFormat.Jpeg);
I.e. by copying the image internally into a Bitmap instance and saving this image instead of the original image, the error disappeared.
I'm assuming that by copying it, the erroneous parts the caused the original Save call to fail are being removed an/or normalized, thus enabling the save operation to succeed.
Interestingly, the so stored image has a smaller file on disk (16 kB) than its original source (26 kB).
First of all make sure, that the desired folder has Read/Write permissions. Changing the permissions solved this problem for me.
Solution is here, you must dispose image object to release the memory on the server.
Try use using statement. Make sure destination directory on server exists too.
The reason may be that the image is loaded lazily and the loading process is not yet finished when you try to save it.
Following what's said in this blog post (assuming you're German by the picture you linked in your question) provides a possible solution. Also this SO question's accepted answer indicates this is due to the fact the image file you're trying to save to is locked.
EDIT
For Ulysses Alves, from the linked blog entry: If you load an image using Image.FromFile() it remains locked until it is disposed of. This prevents calls to Save().
pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
pictureBox1.Image.Save("C:\\test\\test2.jpg");
The above code throws an error.
To make it work, you need to copy the image. The following code works:
pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
Image copy = pictureBox1.Image;
copy.Save("C:\\test\\test2.jpg")
I found this question because I also faced the similar error and the file was actually created with zero length (if you don't see any file, first check the permissions to write into folder as other answers suggest). Although my code was slightly different (I use stream to read the image from memory, not from file), I think my answer may be helpful to anyone facing similar problem.
It may looks counter-intuitive, but you can't really dispose memory stream until you finish with image.
NOT WORKING:
Image patternImage;
using (var ms = new MemoryStream(patternBytes)) {
patternImage = new Bitmap(ms);
}
patternImage.Save(patternFile, ImageFormat.Jpeg);
Just don't dispose the stream until you done with image.
WORKS:
using (var ms = new MemoryStream(patternBytes)) {
patternImage = new Bitmap(ms);
patternImage.Save(patternFile, ImageFormat.Jpeg);
}
What is misleading:
Error message doesn't really tell you anything
You can see the image properties, like width and height, but can't
save it
my solution was to make, write temp content (File.WriteAllText) just before saving the file
Here is the code:
var i = Image.FromFile(i1Path);
File.WriteAllText(i2Path, "empty"); // <---- magic goes here
i.Save(i2Path, ImageFormat.Jpeg);
Please try and let me know
In my case I have accidentally deleted the directory where image was getting stored.
Key Information:
// Using System.Drawing.Imaging:
new Bitmap(image).Save(memoryStream, ImageFormat.Jpeg);
You MUST Cast the Image to a Bitmap to Save it.
Using:
// Using System.Drawing.Imaging:
image.Save(memoryStream, ImageFormat.Jpeg);
WILL throw the Error:
Generic GDI+ error when saving an image
Just use the visual studio as administrator or run the application created by the code as administrator it should work smoothly.
It is user access rights issue.
I faced the same and resolved it by running visual studio as administrator.
In my case, I set validateImageData to false:
Image.FromStream(stream, validateImageData: false);
solution:
Image.FromStream(stream, validateImageData: true);
Open in the program
const string i1Path = #"c:\my\i1.jpg";
const string i2Path = #"c:\my\i2.jpg";
var i = Image.FromFile(i1Path);
i.Save(i2Path, ImageFormat.Jpeg);
i.Dispose();

Generic error occurred in GDI+

I am getting the error "Generic error occurred in GDI+" in my sample code below. What I do is that I make a request to get response for many jpeg files available at live site.
When I get response, I save the file to my application's local folder
and converting these images to binary (bytes of array) so that I can save it into database.
private byte[] GetBinaryImageData(string imgURL)
{
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(imgURL);
WebResponse response = Request.GetResponse();
Stream str = response.GetResponseStream();
System.Drawing.Image objTempImg = System.Drawing.Image.FromStream(str);
objTempImg.Save(FileName, ImageFormat.Jpeg);
FileStream fileStream = new FileStream(FileName, FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[fileStream.Length];
fileStream.Read(buffer, 0, (int)fileStream.Length);
fileStream.Close();
return buffer;
}
i don't get this error for all images, but it occurs for some of images. Anybody know the solution? I have already spent 2 days to oversome this
If I had to guess; it is having problems with handles occasionally, for the reason that you aren't disposing things correctly. This is especially important for things like GDI+. Introduce a few using statements to your code, since pretty much all of those objects are IDisposable:
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(imgURL);
using(WebResponse response = Request.GetResponse())
using(Stream str = response.GetResponseStream())
using(System.Drawing.Image objTempImg = System.Drawing.Image.FromStream(str))
{
objTempImg.Save(FileName, ImageFormat.Jpeg);
}
return File.ReadAllBytes(FileName);
(note I changed your file-reading code too, since it was hugely unreliable; it is incorrect to assume that Stream.Read actually reads all the data; you are supposed to check the return value and loop; but since you want it all, File.ReadAllBytes is easier).
Judging by this article it appears the stream the image is loaded from must exist for the lifetime of the image or bad things can happen.
I've run into this from time to time and found it easiest to copy the image data to a memory stream or a file my process controls and construct the image from that.
From the linked article:
GDI+, and therefore the System.Drawing namespace, may defer the
decoding of raw image bits until the bits are required by the image.
Additionally, even after the image has been decoded, GDI+ may
determine that it is more efficient to discard the memory for a large
Bitmap and to re-decode later. Therefore, GDI+ must have access to the
source bits for the image for the life of the Bitmap or the Image
object.
To retain access to the source bits, GDI+ locks any source file, and
forces the application to maintain the life of any source stream, for
the life of the Bitmap or the Image object.

Implementing IDisposable in File.Delete method?

I want to delete a file on my computer but the app throws an exception that The directory is busy. My first thought is to implement a dispose method in my class which does a file delete.
Knows anybody how to do this?
It's actually a Picturebox which displays a image and if you click on a button beside the image you can remove it, thus deleting it. It's propably that which blocks my filepath.
picobx.Image = Image.FromFile(//path)
Is there any solution to this?
PS. I want to dispose the resource so that I can delete...
do like this:
File.Delete(// path )
The Image.FromFile method actually locks the file thats why you can't delete it. The best would be to read the image as a byte array using bytes = File.ReadBytes(filename), create a memorystream from the byte array, pass the memory stream to Image.FromStream to display the image and then delete it with the button click.
OK - I see what you are tring to do.
Image itself implements IDisposable. You don't need to implement IDisposable yourself, you just have to make sure you either call Dispose on your image, or use the "using" syntax.
The problem is that until the image - the returned reference from Image.FromFile(//path) - is disposed of, it still locks the underlying file so you will always get the exception "The process cannot access the file"
What you intend to do is the following, I suppose:
try {
File.Delete(yourFile);
}
catch (IOException ex) {
// Do something with the exception here, like showing the user that the file can't be deleted)
}
A file that i.e. is in use or in an directory with insufficient rights can't be deleted. So if you get an error, you should look why the file is in use / not accessible, whatever.
Based on your edit, I suggest to do the following:
Image myImage = Image.FromFile(//path);
picobx.Image = myImage;
//click button
picobx.Image = null;
myImage.Dispose();
File.Delete(//path);
As you have suggested you will need to dispose of the image before you can delete it something like this:
Image i = this.pictureBox1.Image;
this.pictureBox1.Image = null;
i.Dispose();
File.Delete(filePath);
Edit I posted this without reading his new edit: I see you're using Image.FromFile.
If the file does not have a valid image format or if GDI+ does not support the pixel format of the file, this method throws an OutOfMemoryException exception.
You're going to have to dispose of the Image before you can delete it.
Image.FromFile
The file remains locked until the Image is disposed.
private Image image;
in your image load function:
image = Image.FromFile("path");
picobx.Image = image;
this goes in your delete method;
image.Dispose;
File.Delete("path");
--- No longer relevant ---
If you're reading the file from a stream and converting the stream to an image. Then you try and delete the file without closing the stream you will get an IOException.
System.IO.IOException: The process cannot access the file 'File Here' because it is being used by another process..
and example of what could be causing this
StreamReader file = new StreamReader(#"C:\trans.txt");
file.ReadToEnd();
File.Delete(#"C:\trans.txt");

Categories

Resources