I just can't for the life of me figure out why I'm getting an out of memory exception here, even after much research on various websites and forums. Is anyone able to shed some light on the root of all evil in this code? The exception is thrown by the call to the Graphics.DrawImage() method, line 79.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditImage(FileModel model)
{
var fileData = new MySite.Models.File(model.FileID, model.ClientID, ConfigurationManager.ConnectionStrings["MySite"].ConnectionString).Data;
Image image = null;
using (var memStream = new MemoryStream())
{
memStream.Write(fileData, 0, fileData.Length);
image = Image.FromStream(memStream);
}
using (var graphics = Graphics.FromImage(image))
{
graphics.DrawImage(image, model.x1, model.y1, (model.x2 - model.x1), (model.y2 - model.y1));
graphics.Save();
}
using (var memStream = new MemoryStream())
{
image.Save(memStream, System.Drawing.Imaging.ImageFormat.Jpeg);
return File(memStream.ToArray(), "image/jpeg");
}
}
Stack trace:
[OutOfMemoryException: Out of memory.]
System.Drawing.Graphics.CheckErrorStatus(Int32 status) +1143476
System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y, Int32 width, Int32 height) +141
ProPilot.Controllers.DocumentsController.EditImage(FileModel model) in C:\DEV\Web\Controllers\DocumentsController.cs:79
lambda_method(Closure , ControllerBase , Object[] ) +104
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +14
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +211
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
System.Web.Mvc.Async.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41() +28
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +10
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +57
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +48
System.Web.Mvc.Async.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33() +57
System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +223
System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__36(IAsyncResult asyncResult) +10
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +57
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +48
System.Web.Mvc.Async.<>c__DisplayClass2a.<BeginInvokeAction>b__20() +24
System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult) +102
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +57
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +43
System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult) +14
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +57
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +47
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult) +25
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +47
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9629296
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
@Sayse hit the nail on the head with his comment above.
When using Image.FromStream:
You must keep the stream open for the lifetime of the Image.
Source: http://msdn.microsoft.com/en-AU/library/93z9ee4x.aspx
using (var memStream = new MemoryStream())
{
memStream.Write(fileData, 0, fileData.Length);
using(Image image = Image.FromStream(memStream))
{
using (var graphics = Graphics.FromImage(image))
{
graphics.DrawImage(image, model.x1, model.y1, (model.x2 - model.x1), (model.y2 - model.y1));
graphics.Save();
}
using (var outStream = new MemoryStream())
{
image.Save(outStream, System.Drawing.Imaging.ImageFormat.Jpeg);
return File(outStream.ToArray(), "image/jpeg");
}
}
}
If you work with the Bitmap
or Image
class you will find several problems.
If you load an image with Image.FromFile()
the framework does not close the file handle after loading the image. The file stays open until the garbage collector recollects the image. This is a severe misdesign in GDI+. There is no need to keep a file open after the image has already been read into memory.
So I tried to work around this problem by using Image.FromStream()
instead. But this is no solution because when the stream has been closed a DrawImage()
operation on that image will fail with "Out of memory".
So if you want to read for example a Bitmap from a file and you want to assure that the file handle is closed you have to use an ugly workaround like this:
Bitmap ReadBitmapFromFile(String s_Path)
{
using (FileStream i_Stream = new FileStream(s_Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (Bitmap i_Bmp = new Bitmap(i_Stream))
{
return new Bitmap(i_Bmp);
}
}
}
By the way: Image.Clone()
produces a similar problem.
Instead of
Bitmap i_Clone = (Bitmap)i_Bmp.Clone()
I had to use:
Bitmap i_Clone = new Bitmap(i_Bmp);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With