No exception thrown on Image.Save when no free space on disk - c#

I am saving images on the server and I am covering the scenario where the disk has no available free space. To simulate that, I created an almost-no-space virtual hard drive and I am trying to save the images there.
I was expecting to receive an exception when trying to save the image, with:
using (var ms = new MemoryStream(myPictureStream))
using (Image image = Image.FromStream(ms))
{
image.Save(fileName, ImageFormat.Jpeg);
}
But that is not the case, there is no exception thrown. Instead, an empty file is saved, so I can not be aware of the error.
I would like to receive some error when trying to save the image, or being able to anticipate the error before an exception is thrown.
I already considered some possible solutions:
I know I can check the available free space on a drive with DriveInfo, but I will not have the drive letter to check that on production, so this option is discarded.
After saving the image, I try to retrieve it with Image.FromFile and then an OutOfMemoryException is thrown, but I know that this can happen in other scenarios, and I am not sure if this is a legitimate way of checking that the disk has no free space.
So... Any idea why Image.Save() does not throw errors when no free space available? Any suggestion on how to handle this scenario?
EDIT
GetLastError does not help (it is code 0, success), so it seems that the best way to handle this scenario is saving the image to a temporal MemoryStream and then writing it into a file, as pointed out by answers given by #Eldar and #nvoigt.
Doing it this way, a IOException is thrown, and you can check the HResult of the exception to see if it is a not-enough-free-space-in-disk easy (see this answer: https://stackoverflow.com/a/9294382/4067893).

Image.Save() internally calls a GDI function GdipSaveImageToFile() that doesn't fail if the drive space is insufficient. You may give a try to GetLastError() to get the last Win32 error.
If GetLastError() doesn't help, the only solution that comes up in my mind is saving the image to a temporal MemoryStream, then writing the resulting stream into a file.

You will need to do the stream handling yourself:
using (var ms = new MemoryStream(myPictureStream))
using (Image image = Image.FromStream(ms))
using(var jpg = new MemoryStream())
using (var file = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
image.Save(jpg, ImageFormat.Jpeg);
jpg.Seek(0, SeekOrigin.Begin);
jpg.WriteTo(file);
}

Related

Cannot save array of png images: "generic error in GDI+" [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();

Generic occured on GDI+ [duplicate]

This seems to be a bit of an infamous error all over the web. So much so that I have been unable to find an answer to my problem as my scenario doesn't fit. An exception gets thrown when I save the image to the stream.
Weirdly this works perfectly with a png but gives the above error with jpg and gif which is rather confusing.
Most similar problem out there relate to saving images to files without permissions. Ironically the solution is to use a memory stream as I am doing....
public static byte[] ConvertImageToByteArray(Image imageToConvert)
{
using (var ms = new MemoryStream())
{
ImageFormat format;
switch (imageToConvert.MimeType())
{
case "image/png":
format = ImageFormat.Png;
break;
case "image/gif":
format = ImageFormat.Gif;
break;
default:
format = ImageFormat.Jpeg;
break;
}
imageToConvert.Save(ms, format);
return ms.ToArray();
}
}
More detail to the exception. The reason this causes so many issues is the lack of explanation :(
System.Runtime.InteropServices.ExternalException was unhandled by user code
Message="A generic error occurred in GDI+."
Source="System.Drawing"
ErrorCode=-2147467259
StackTrace:
at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
at System.Drawing.Image.Save(Stream stream, ImageFormat format)
at Caldoo.Infrastructure.PhotoEditor.ConvertImageToByteArray(Image imageToConvert) in C:\Users\Ian\SVN\Caldoo\Caldoo.Coordinator\PhotoEditor.cs:line 139
at Caldoo.Web.Controllers.PictureController.Croppable() in C:\Users\Ian\SVN\Caldoo\Caldoo.Web\Controllers\PictureController.cs:line 132
at lambda_method(ExecutionScope , ControllerBase , Object[] )
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
InnerException:
OK things I have tried so far.
Cloning the image and working on that.
Retrieving the encoder for that MIME passing that with jpeg quality setting.
OK I seem to have found the cause just by sheer luck and its nothing wrong with that particular method, it's further back up the call stack.
Earlier I resize the image and as part of that method I return the resized object as follows. I have inserted two calls to the above method and a direct save to a file.
// At this point the new bitmap has no MimeType
// Need to output to memory stream
using (var m = new MemoryStream())
{
dst.Save(m, format);
var img = Image.FromStream(m);
//TEST
img.Save("C:\\test.jpg");
var bytes = PhotoEditor.ConvertImageToByteArray(img);
return img;
}
It appears that the memory stream that the object was created on has to be open at the time the object is saved. I am not sure why this is. Is anyone able to enlighten me and how I can get around this.
I only return from a stream because after using the resize code similar to this the destination file has an unknown mime type (img.RawFormat.Guid) and Id like the Mime type to be correct on all image objects as it makes it hard write generic handling code otherwise.
EDIT
This didn't come up in my initial search but here's the answer from Jon Skeet
If you are getting that error , then I can say that your application doesn't have a write permission on some directory.
For example, if you are trying to save the Image from the memory stream to the file system , you may get that error.
Please if you are using XP, make sure to add write permission for the aspnet account on that folder.
If you are using windows server (2003,2008) or Vista, make sure that add write permission for the Network service account.
Hope it help some one.
I'll add this cause of the error as well in hopes it helps some future internet traveler. :)
GDI+ limits the maximum height of an image to 65500
We do some basic image resizing, but in resizing we try to maintain aspect ratio. We have a QA guy who's a little too good at this job; he decided to test this with a ONE pixel wide photo that was 480 pixels tall. When the image was scaled to meet our dimensions, the height was north of 68,000 pixels and our app exploded with A generic error occurred in GDI+.
You can verify this yourself with test:
int width = 480;
var height = UInt16.MaxValue - 36; //succeeds at 65499, 65500
try
{
while(true)
{
var image = new Bitmap(width, height);
using(MemoryStream ms = new MemoryStream())
{
//error will throw from here
image.Save(ms, ImageFormat.Jpeg);
}
height += 1;
}
}
catch(Exception ex)
{
//explodes at 65501 with "A generic error occurred in GDI+."
}
It's too bad there's not a friendly .net ArgumentException thrown in the constructor of Bitmap.
This article explains in detail what exactly happens: Bitmap and Image constructor dependencies
In short, for a lifetime of an Image constructed from a stream, the stream must not be destroyed.
So, instead of
using (var strm = new ... ) {
myImage = Image.FromStream(strm);
}
try this
Stream imageStream;
...
imageStream = new ...;
myImage = Image.FromStream(strm);
and close imageStream at the form close or web page close.
You'll also get this exception if you try to save to an invalid path or if there's a permissions issue.
If you're not 100% sure that the file path is available and permissions are correct then try writing a to a text file. This takes just a few seconds to rule out what would be a very simple fix.
var img = System.Drawing.Image.FromStream(incomingStream);
// img.Save(path);
System.IO.File.WriteAllText(path, "Testing valid path & permissions.");
And don't forget to clean up your file.
Save image to bitmap variable
using (var ms = new MemoryStream())
{
Bitmap bmp = new Bitmap(imageToConvert);
bmp.Save(ms, format);
return ms.ToArray();
}
Just in case if someone is doing as stupid stuff as I was.
1. make sure path does exist.
2. make sure you have permissions to write.
3. make sure your path is correct, in my case I was missing file name in the TargetPath :(
it should have said, your path sucks than "A generic error occurred in GDI+"
I also got this error when saving JPEGs, but only for certain images.
My final code:
try
{
img.SaveJpeg(tmpFile, quality); // This is always successful for say image1.jpg, but always throws the GDI+ exception for image2.jpg
}
catch (Exception ex)
{
// Try HU's method: Convert it to a Bitmap first
img = new Bitmap(img);
img.SaveJpeg(tmpFile, quality); // This is always successful
}
I didn't create the images so I can't tell what the difference is.
I'd appreciate if anyone could explain that.
This is my SaveJpeg function just FYI:
private static void SaveJpeg(this Image img, string filename, int quality)
{
EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, (long)quality);
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
img.Save(filename, jpegCodec, encoderParams);
}
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
var encoders = ImageCodecInfo.GetImageEncoders();
var encoder = encoders.SingleOrDefault(c => string.Equals(c.MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase));
if (encoder == null) throw new Exception($"Encoder not found for mime type {mimeType}");
return encoder;
}
I found that if one of the parent folders where I was saving the file had a trailing space then GDI+ would throw the generic exception.
In other words, if I tried to save to "C:\Documents and Settings\myusername\Local Settings\Temp\ABC DEF M1 Trended Values \Images\picture.png" then it threw the generic exception.
My folder name was being generated from a file name that happened to have a trailing space so it was easy to .Trim() that and move on.
if your code is as follows then also this error occurs
private Image GetImage(byte[] byteArray)
{
using (var stream = new MemoryStream(byteArray))
{
return Image.FromStream(stream);
}
}
The correct one is
private Image GetImage(byte[] byteArray)
{
var stream = new MemoryStream(byteArray))
return Image.FromStream(stream);
}
This may be because we are returning from the using block
Had a very similar problem and also tried cloning the image which doesn't work. I found that the best solution was to create a new Bitmap object from the image that was loaded from the memory stream. That way the stream can be disposed of e.g.
using (var m = new MemoryStream())
{
var img = new Bitmap(Image.FromStream(m));
return img;
}
Hope this helps.
This is an expansion / qualification of Fred's response which stated: "GDI limits the height of an image to 65534". We ran into this issue with one of our .NET applications, and having seen the post, our outsourcing team raised their hands in the air and said they couldn't fix the problem without major changes.
Based on my testing, it's possible to create / manipulate images with a height larger than 65534, but the issue arises when saving to a stream or file IN CERTAIN FORMATS. In the following code, the t.Save() method call throws our friend the generic exception when the pixel height is 65501 for me. For reasons of curiosity, I repeated the test for width, and the same limit applied to saving.
for (int i = 65498; i <= 100000; i++)
{
using (Bitmap t = new Bitmap(800, i))
using (Graphics gBmp = Graphics.FromImage(t))
{
Color green = Color.FromArgb(0x40, 0, 0xff, 0);
using (Brush greenBrush = new SolidBrush(green))
{
// draw a green rectangle to the bitmap in memory
gBmp.FillRectangle(greenBrush, 0, 0, 799, i);
if (File.Exists("c:\\temp\\i.jpg"))
{
File.Delete("c:\\temp\\i.jpg");
}
t.Save("c:\\temp\\i.jpg", ImageFormat.Jpeg);
}
}
GC.Collect();
}
The same error also occurs if you write to a memory stream.
To get round it, you can repeat the above code and substitute ImageFormat.Tiff or ImageFormat.Bmp for ImageFormat.Jpeg.
This runs up to heights / widths of 100,000 for me - I didn't test the limits. As it happens .Tiff was a viable option for us.
BE WARNED
The in memory TIFF streams / files consume more memory than their JPG counterparts.
Error occurring because of Permission. make sure folder have ALL THE PERMISSION.
public Image Base64ToImage(string base64String)
{
// Convert Base64 String to byte[]
byte[] imageBytes = Convert.FromBase64String(base64String);
MemoryStream ms = new MemoryStream(imageBytes, 0,
imageBytes.Length);
// Convert byte[] to Image
ms.Write(imageBytes, 0, imageBytes.Length);
Image image = Image.FromStream(ms, true);
return image;
}
img.Save("YOUR PATH TO SAVE IMAGE")
SOLVED - I had this exact problem. The fix, for me, was to up the disk quota for IUSR on the IIS server. In this instance, we have a catalog app with images of items and such. The upload quota for the "Anonymous Web User" was set to 100MB, which is the default for this particular hosting company's IIS servers. I upped it to 400MB and was able to upload images without error.
This might not be your issue, but if it is, it's an easy fix.
In my case the problem was in the path I was saving (the root C:\). Changing it to D:\111\ made the exception go away.
Another cause for this error - the path you indicate in the Save method of the Bitmap instance doesn't exist or you haven't supplied a full / valid path.
Just had this error because I was passing in a filename and not a full path!
It happens!
My turn!
using (System.Drawing.Image img = Bitmap.FromFile(fileName))
{
... do some manipulation of img ...
img.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
}
Got it on the .Save... because the using() is holding the file open, so I can't overwrite it. Maybe this will help someone in the future.
Same problem I was facing. But in my case, I was trying to save file in C drive and it was not accessible. So I tried it to save in D drive which was fully accessible and I succeeded.
So first check your folders in which you are trying to save. You must have all (read and write) rights for that particular folder.
Simple, create a new instance of Bitmap solves the problem.
string imagePath = Path.Combine(Environment.CurrentDirectory, $"Bhatti{i}.png");
Bitmap bitmap = new Bitmap(image);
bitmap.Save(imagePath);
I notice that your "jpeg" case is actually:
default:
format = ImageFormat.Jpeg;
break;
Are you sure that the format is jpeg and not something else?
I'd try:
case "image/jpg": // or "image/jpeg" !
format = ImageFormat.Jpeg;
break;
Or check what imageToConvert.MimeType() is actually returning.
UPDATE
Is there any other initialisation you need to do to the MemoryStream object?
I had this issue on a test server but not on the live server.
I was writing the image to a stream, so it wasn't a permission issue.
I'd been directly deploying some of the .dll's to the test server.
Deploying the entire solution fixed the issue, so it was probably a weird compilation mismatch
Just to throw another possible solution on the pile, I'll mention the case I ran into with this error message. The method Bitmap.Save would throw this exception when saving an bitmap I had transformed and was displaying. I discovered it would not throw the exception if the statement had a breakpoint on it, nor would it if the Bitmap.Save was preceeded by Thread.Sleep(500) so I suppose there is some sort of resource contention going on.
Simply copying the image to a new Bitmap object was enough to prevent this exception from appearing:
new Bitmap(oldbitmap).Save(filename);
One other cause of this error and that solve my problème is that your application doesn't have a write permission on some directory.
so to complete the answer of savindra : https://stackoverflow.com/a/7426516/6444829.
Here is how you Grant File Access to IIS_IUSERS
To provide access to an ASP.NET application, you must grant access to the IIs_IUSERS.
To grant read, write, and modify permissions to a specific File or Folder
In Windows Explorer, locate and select the required file.
Right click the file, and then click Properties.
In the Properties dialog box, click the Security tab.
On the Security tab, examine the list of users.
(If your application is running as a Network Service, add the network service account in the list and grant it the permission.
In the Properties dialog box, click IIs_IUSERS, and in the Permissions for NETWORK SERVICE section, select the Read, Write, and Modify permissions.
Click Apply, and then click OK.
this worked for me in my IIS of windows server 2016 and local IIS windows 10.
We had a similar problem on generating a PDF or resize image using ImageProcessor lib on production server.
Recycle the application pool fix the issue.
If you are trying to save an image to a remote location be sure to add the NETWORK_SERVICE user account into the security settings and give that user read and write permissions. Otherwise it is not going to work.
byte[] bts = (byte[])page1.EnhMetaFileBits;
using (var ms = new MemoryStream(bts))
{
var image = System.Drawing.Image.FromStream(ms);
System.Drawing.Image img = image.GetThumbnailImage(200, 260, null, IntPtr.Zero);
img.Save(NewPath, System.Drawing.Imaging.ImageFormat.Png);
}
I also get this error because i'm trying to save images with the same name of previous saved images.
Make sure that you don't save images with duplicate name.
Use for thar for example a 'Random' function (How does C#'s random number generator work?)
or for example generate a Guid (http://betterexplained.com/articles/the-quick-guide-to-guids/)
in my case, path was wrong
just use this
String path = Server.MapPath("~/last_img.png");//Path
For me I was using the Image.Save(Stream, ImageCodecInfo, EncoderParameters) and apparently this was causing the infamous A generic error occurred in GDI+ error.
I was trying to use EncoderParameter to save the jpegs in 100% quality. This was working perfectly on "my machine" (doh!) and not on production.
When I used the Image.Save(Stream, ImageFormat) instead, the error disappeared! So like an idiot I continued to use the latter although it saves them in default quality which I assume is just 50%.
Hope this info helps someone.
I encountered the problem too. The problem was due to the loading stream being disposed. But I did not dispose it, it was inside .Net framework. All I had to do was use:
image_instance = Image.FromFile(file_name);
instead of
image_instance.Load(file_name);
image_instance is of type System.Windows.Forms.PictureBox!
PictureBox's Load() disposes the stream which the image was loaded from, and I did not know that.

Image opened from stream is different from opened from file

I have 2 pieces of sample codes, I try to do the same thing that update property items back to image and save it under a different file.
Code 1
Image image;
using (FileStream stream = new FileStream(this.fileName, FileMode.Open)) {
image = Image.FromStream(stream);
foreach (var property in this.propItems) {
image.SetPropertyItem(property);
}
}
image.Save(#"D:\Temp\1.jpg");
image.Dispose();
Code 2
using (Image image = new Bitmap(this.fileName)) {
foreach (var property in this.propItems) {
image.SetPropertyItem(property);
}
image.Save(#"D:\Temp\1.jpg");
}
The only difference is that how I opened the file. If I run the first piece of code I got exception message
System.Runtime.InteropServices.ExternalException was unhandled
HResult=-2147467259 Message=A generic error occurred in GDI+.
Source=System.Drawing ErrorCode=-2147467259
My 2nd piece of code just runs fine, I can get proper output. What is the difference here?
This is by design, the MSDN article for Image.FromStream() sternly warns about this. Once you close the stream, the image is no longer usable. And trying to save it like you do is very likely, but not guaranteed, to throw an exception when it tries to retrieve pixel data from a closed stream.
A key property of the Image class is that it is lazy, not unlike many .NET classes, it won't access the stream data until necessary. And it isn't necessary until the pixel data is actually used, that happens in the Save() call in your snippet. Kaboom when it can no longer read it.
You can fix your first snippet by moving the Save() call inside the using statement:
using (var stream = new FileStream(this.fileName, FileMode.Open))
using (var image = Image.FromStream(stream) {
foreach (var property in this.propItems) {
image.SetPropertyItem(property);
}
image.Save(#"D:\Temp\1.jpg");
}
No point in using a FileStream anymore. Do note that the file that you save the image to cannot be the same file that you read the image from. Some hint that you tried to work around that problem. Using a MemoryStream is a common technique to avoid the lock on the file.
Also note another bug in your code, you save the file with the .jpg extension but it is actually a PNG. You cannot omit the ImageFormat argument if you want a JPEG.

C# A generic error occurred in GDI+ exceptional error [duplicate]

This seems to be a bit of an infamous error all over the web. So much so that I have been unable to find an answer to my problem as my scenario doesn't fit. An exception gets thrown when I save the image to the stream.
Weirdly this works perfectly with a png but gives the above error with jpg and gif which is rather confusing.
Most similar problem out there relate to saving images to files without permissions. Ironically the solution is to use a memory stream as I am doing....
public static byte[] ConvertImageToByteArray(Image imageToConvert)
{
using (var ms = new MemoryStream())
{
ImageFormat format;
switch (imageToConvert.MimeType())
{
case "image/png":
format = ImageFormat.Png;
break;
case "image/gif":
format = ImageFormat.Gif;
break;
default:
format = ImageFormat.Jpeg;
break;
}
imageToConvert.Save(ms, format);
return ms.ToArray();
}
}
More detail to the exception. The reason this causes so many issues is the lack of explanation :(
System.Runtime.InteropServices.ExternalException was unhandled by user code
Message="A generic error occurred in GDI+."
Source="System.Drawing"
ErrorCode=-2147467259
StackTrace:
at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
at System.Drawing.Image.Save(Stream stream, ImageFormat format)
at Caldoo.Infrastructure.PhotoEditor.ConvertImageToByteArray(Image imageToConvert) in C:\Users\Ian\SVN\Caldoo\Caldoo.Coordinator\PhotoEditor.cs:line 139
at Caldoo.Web.Controllers.PictureController.Croppable() in C:\Users\Ian\SVN\Caldoo\Caldoo.Web\Controllers\PictureController.cs:line 132
at lambda_method(ExecutionScope , ControllerBase , Object[] )
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
InnerException:
OK things I have tried so far.
Cloning the image and working on that.
Retrieving the encoder for that MIME passing that with jpeg quality setting.
OK I seem to have found the cause just by sheer luck and its nothing wrong with that particular method, it's further back up the call stack.
Earlier I resize the image and as part of that method I return the resized object as follows. I have inserted two calls to the above method and a direct save to a file.
// At this point the new bitmap has no MimeType
// Need to output to memory stream
using (var m = new MemoryStream())
{
dst.Save(m, format);
var img = Image.FromStream(m);
//TEST
img.Save("C:\\test.jpg");
var bytes = PhotoEditor.ConvertImageToByteArray(img);
return img;
}
It appears that the memory stream that the object was created on has to be open at the time the object is saved. I am not sure why this is. Is anyone able to enlighten me and how I can get around this.
I only return from a stream because after using the resize code similar to this the destination file has an unknown mime type (img.RawFormat.Guid) and Id like the Mime type to be correct on all image objects as it makes it hard write generic handling code otherwise.
EDIT
This didn't come up in my initial search but here's the answer from Jon Skeet
If you are getting that error , then I can say that your application doesn't have a write permission on some directory.
For example, if you are trying to save the Image from the memory stream to the file system , you may get that error.
Please if you are using XP, make sure to add write permission for the aspnet account on that folder.
If you are using windows server (2003,2008) or Vista, make sure that add write permission for the Network service account.
Hope it help some one.
I'll add this cause of the error as well in hopes it helps some future internet traveler. :)
GDI+ limits the maximum height of an image to 65500
We do some basic image resizing, but in resizing we try to maintain aspect ratio. We have a QA guy who's a little too good at this job; he decided to test this with a ONE pixel wide photo that was 480 pixels tall. When the image was scaled to meet our dimensions, the height was north of 68,000 pixels and our app exploded with A generic error occurred in GDI+.
You can verify this yourself with test:
int width = 480;
var height = UInt16.MaxValue - 36; //succeeds at 65499, 65500
try
{
while(true)
{
var image = new Bitmap(width, height);
using(MemoryStream ms = new MemoryStream())
{
//error will throw from here
image.Save(ms, ImageFormat.Jpeg);
}
height += 1;
}
}
catch(Exception ex)
{
//explodes at 65501 with "A generic error occurred in GDI+."
}
It's too bad there's not a friendly .net ArgumentException thrown in the constructor of Bitmap.
This article explains in detail what exactly happens: Bitmap and Image constructor dependencies
In short, for a lifetime of an Image constructed from a stream, the stream must not be destroyed.
So, instead of
using (var strm = new ... ) {
myImage = Image.FromStream(strm);
}
try this
Stream imageStream;
...
imageStream = new ...;
myImage = Image.FromStream(strm);
and close imageStream at the form close or web page close.
You'll also get this exception if you try to save to an invalid path or if there's a permissions issue.
If you're not 100% sure that the file path is available and permissions are correct then try writing a to a text file. This takes just a few seconds to rule out what would be a very simple fix.
var img = System.Drawing.Image.FromStream(incomingStream);
// img.Save(path);
System.IO.File.WriteAllText(path, "Testing valid path & permissions.");
And don't forget to clean up your file.
Save image to bitmap variable
using (var ms = new MemoryStream())
{
Bitmap bmp = new Bitmap(imageToConvert);
bmp.Save(ms, format);
return ms.ToArray();
}
Just in case if someone is doing as stupid stuff as I was.
1. make sure path does exist.
2. make sure you have permissions to write.
3. make sure your path is correct, in my case I was missing file name in the TargetPath :(
it should have said, your path sucks than "A generic error occurred in GDI+"
I also got this error when saving JPEGs, but only for certain images.
My final code:
try
{
img.SaveJpeg(tmpFile, quality); // This is always successful for say image1.jpg, but always throws the GDI+ exception for image2.jpg
}
catch (Exception ex)
{
// Try HU's method: Convert it to a Bitmap first
img = new Bitmap(img);
img.SaveJpeg(tmpFile, quality); // This is always successful
}
I didn't create the images so I can't tell what the difference is.
I'd appreciate if anyone could explain that.
This is my SaveJpeg function just FYI:
private static void SaveJpeg(this Image img, string filename, int quality)
{
EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, (long)quality);
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
img.Save(filename, jpegCodec, encoderParams);
}
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
var encoders = ImageCodecInfo.GetImageEncoders();
var encoder = encoders.SingleOrDefault(c => string.Equals(c.MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase));
if (encoder == null) throw new Exception($"Encoder not found for mime type {mimeType}");
return encoder;
}
I found that if one of the parent folders where I was saving the file had a trailing space then GDI+ would throw the generic exception.
In other words, if I tried to save to "C:\Documents and Settings\myusername\Local Settings\Temp\ABC DEF M1 Trended Values \Images\picture.png" then it threw the generic exception.
My folder name was being generated from a file name that happened to have a trailing space so it was easy to .Trim() that and move on.
if your code is as follows then also this error occurs
private Image GetImage(byte[] byteArray)
{
using (var stream = new MemoryStream(byteArray))
{
return Image.FromStream(stream);
}
}
The correct one is
private Image GetImage(byte[] byteArray)
{
var stream = new MemoryStream(byteArray))
return Image.FromStream(stream);
}
This may be because we are returning from the using block
Had a very similar problem and also tried cloning the image which doesn't work. I found that the best solution was to create a new Bitmap object from the image that was loaded from the memory stream. That way the stream can be disposed of e.g.
using (var m = new MemoryStream())
{
var img = new Bitmap(Image.FromStream(m));
return img;
}
Hope this helps.
This is an expansion / qualification of Fred's response which stated: "GDI limits the height of an image to 65534". We ran into this issue with one of our .NET applications, and having seen the post, our outsourcing team raised their hands in the air and said they couldn't fix the problem without major changes.
Based on my testing, it's possible to create / manipulate images with a height larger than 65534, but the issue arises when saving to a stream or file IN CERTAIN FORMATS. In the following code, the t.Save() method call throws our friend the generic exception when the pixel height is 65501 for me. For reasons of curiosity, I repeated the test for width, and the same limit applied to saving.
for (int i = 65498; i <= 100000; i++)
{
using (Bitmap t = new Bitmap(800, i))
using (Graphics gBmp = Graphics.FromImage(t))
{
Color green = Color.FromArgb(0x40, 0, 0xff, 0);
using (Brush greenBrush = new SolidBrush(green))
{
// draw a green rectangle to the bitmap in memory
gBmp.FillRectangle(greenBrush, 0, 0, 799, i);
if (File.Exists("c:\\temp\\i.jpg"))
{
File.Delete("c:\\temp\\i.jpg");
}
t.Save("c:\\temp\\i.jpg", ImageFormat.Jpeg);
}
}
GC.Collect();
}
The same error also occurs if you write to a memory stream.
To get round it, you can repeat the above code and substitute ImageFormat.Tiff or ImageFormat.Bmp for ImageFormat.Jpeg.
This runs up to heights / widths of 100,000 for me - I didn't test the limits. As it happens .Tiff was a viable option for us.
BE WARNED
The in memory TIFF streams / files consume more memory than their JPG counterparts.
Error occurring because of Permission. make sure folder have ALL THE PERMISSION.
public Image Base64ToImage(string base64String)
{
// Convert Base64 String to byte[]
byte[] imageBytes = Convert.FromBase64String(base64String);
MemoryStream ms = new MemoryStream(imageBytes, 0,
imageBytes.Length);
// Convert byte[] to Image
ms.Write(imageBytes, 0, imageBytes.Length);
Image image = Image.FromStream(ms, true);
return image;
}
img.Save("YOUR PATH TO SAVE IMAGE")
SOLVED - I had this exact problem. The fix, for me, was to up the disk quota for IUSR on the IIS server. In this instance, we have a catalog app with images of items and such. The upload quota for the "Anonymous Web User" was set to 100MB, which is the default for this particular hosting company's IIS servers. I upped it to 400MB and was able to upload images without error.
This might not be your issue, but if it is, it's an easy fix.
In my case the problem was in the path I was saving (the root C:\). Changing it to D:\111\ made the exception go away.
Another cause for this error - the path you indicate in the Save method of the Bitmap instance doesn't exist or you haven't supplied a full / valid path.
Just had this error because I was passing in a filename and not a full path!
It happens!
My turn!
using (System.Drawing.Image img = Bitmap.FromFile(fileName))
{
... do some manipulation of img ...
img.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
}
Got it on the .Save... because the using() is holding the file open, so I can't overwrite it. Maybe this will help someone in the future.
Same problem I was facing. But in my case, I was trying to save file in C drive and it was not accessible. So I tried it to save in D drive which was fully accessible and I succeeded.
So first check your folders in which you are trying to save. You must have all (read and write) rights for that particular folder.
Simple, create a new instance of Bitmap solves the problem.
string imagePath = Path.Combine(Environment.CurrentDirectory, $"Bhatti{i}.png");
Bitmap bitmap = new Bitmap(image);
bitmap.Save(imagePath);
I notice that your "jpeg" case is actually:
default:
format = ImageFormat.Jpeg;
break;
Are you sure that the format is jpeg and not something else?
I'd try:
case "image/jpg": // or "image/jpeg" !
format = ImageFormat.Jpeg;
break;
Or check what imageToConvert.MimeType() is actually returning.
UPDATE
Is there any other initialisation you need to do to the MemoryStream object?
I had this issue on a test server but not on the live server.
I was writing the image to a stream, so it wasn't a permission issue.
I'd been directly deploying some of the .dll's to the test server.
Deploying the entire solution fixed the issue, so it was probably a weird compilation mismatch
Just to throw another possible solution on the pile, I'll mention the case I ran into with this error message. The method Bitmap.Save would throw this exception when saving an bitmap I had transformed and was displaying. I discovered it would not throw the exception if the statement had a breakpoint on it, nor would it if the Bitmap.Save was preceeded by Thread.Sleep(500) so I suppose there is some sort of resource contention going on.
Simply copying the image to a new Bitmap object was enough to prevent this exception from appearing:
new Bitmap(oldbitmap).Save(filename);
One other cause of this error and that solve my problème is that your application doesn't have a write permission on some directory.
so to complete the answer of savindra : https://stackoverflow.com/a/7426516/6444829.
Here is how you Grant File Access to IIS_IUSERS
To provide access to an ASP.NET application, you must grant access to the IIs_IUSERS.
To grant read, write, and modify permissions to a specific File or Folder
In Windows Explorer, locate and select the required file.
Right click the file, and then click Properties.
In the Properties dialog box, click the Security tab.
On the Security tab, examine the list of users.
(If your application is running as a Network Service, add the network service account in the list and grant it the permission.
In the Properties dialog box, click IIs_IUSERS, and in the Permissions for NETWORK SERVICE section, select the Read, Write, and Modify permissions.
Click Apply, and then click OK.
this worked for me in my IIS of windows server 2016 and local IIS windows 10.
We had a similar problem on generating a PDF or resize image using ImageProcessor lib on production server.
Recycle the application pool fix the issue.
If you are trying to save an image to a remote location be sure to add the NETWORK_SERVICE user account into the security settings and give that user read and write permissions. Otherwise it is not going to work.
byte[] bts = (byte[])page1.EnhMetaFileBits;
using (var ms = new MemoryStream(bts))
{
var image = System.Drawing.Image.FromStream(ms);
System.Drawing.Image img = image.GetThumbnailImage(200, 260, null, IntPtr.Zero);
img.Save(NewPath, System.Drawing.Imaging.ImageFormat.Png);
}
I also get this error because i'm trying to save images with the same name of previous saved images.
Make sure that you don't save images with duplicate name.
Use for thar for example a 'Random' function (How does C#'s random number generator work?)
or for example generate a Guid (http://betterexplained.com/articles/the-quick-guide-to-guids/)
in my case, path was wrong
just use this
String path = Server.MapPath("~/last_img.png");//Path
For me I was using the Image.Save(Stream, ImageCodecInfo, EncoderParameters) and apparently this was causing the infamous A generic error occurred in GDI+ error.
I was trying to use EncoderParameter to save the jpegs in 100% quality. This was working perfectly on "my machine" (doh!) and not on production.
When I used the Image.Save(Stream, ImageFormat) instead, the error disappeared! So like an idiot I continued to use the latter although it saves them in default quality which I assume is just 50%.
Hope this info helps someone.
I encountered the problem too. The problem was due to the loading stream being disposed. But I did not dispose it, it was inside .Net framework. All I had to do was use:
image_instance = Image.FromFile(file_name);
instead of
image_instance.Load(file_name);
image_instance is of type System.Windows.Forms.PictureBox!
PictureBox's Load() disposes the stream which the image was loaded from, and I did not know that.

Loading an image from a stream without keeping the stream open

Is it possible to use the FromStream method of System.Drawing.Image without having to keep the stream open for the lifetime of the image?
I have an application which loads a bunch of toolbar graphics from resource files, using a combination of Image.FromStream and Assembly.GetManifestResourceStream.
The problem I'm having is while this works fine on Windows 7, on Windows XP the application crashes if a user interface element linked to one of these images is disabled. On Windows 7, the image is rendered in grayscale. On XP, it crashes with an out of memory exception.
After a load of hairpulling I have finally traced it to the initial loading of the image. As a matter of course, if I create any object implementing IDisposable that is also destroyed in the same method, I wrap it in a using statement, for example
using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
{
image = Image.FromStream(resourceStream);
}
If I remove the using statement so that the stream isn't disposed, then the application no longer crashes on XP. But I now have a bunch of "orphan" streams hanging about - the images are stored in command classes and these correctly dispose of the images when they themselves are disposed, but the original stream isn't.
I checked the documentation for FromStream and it confirms the stream needs to remain open. Why this hasn't crashed and burned on the Windows 7 development system is a mystery however!
I really don't want this stream hanging around, and I certainly don't want to have to store a reference to this stream as well as the image so I can dispose of it later. I only have need of that stream once so I want to get rid of it :)
Is it possible to create the image and then kill of the stream there and then?
The reason the stream needs to be open is the following:
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.
The documented workaround is to create either a non-indexed image using Graphics.DrawImage or to create an indexed Bitmap from the original image as described here:
Bitmap and Image constructor dependencies
According to the documentation of Image.FromStream, the stream must be kept open while the image is in use. Therefore, even if closing worked (and there's nothing to say you can't close a stream before it's disposed, as far as the stream object itself goes) it may not be a very reliable approach.
You could copy the image to another image object, and use that. However, this is likely to be more memory intensive than just keeping the stream open.
You could save the stream to a temporary file and use the Image.FromFile method. Or simply don't embed the image, keep it as a file and load it from this file at runtime.
I'am sure this will help someone :)
I used it for my dataGridView_SelectionChanged:
private void dataGridViewAnzeige_SelectionChanged(object sender, EventArgs e)
{
var imageAsByteArray = File.ReadAllBytes(path);
pictureBox1.Image = byteArrayToImage(imageAsByteArray);
}
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}

Categories