异步上传和调整多个图像大小时出现内存不足异常

Posted

技术标签:

【中文标题】异步上传和调整多个图像大小时出现内存不足异常【英文标题】:Out of Memory exception while uploading and resizing multiple images asynchronously 【发布时间】:2016-02-25 05:02:51 【问题描述】:

您好,我正在尝试异步上传一些图像,出现内存不足异常,我正在通过 using 语句进行处理,但是我得到了以下堆栈跟踪

[OutOfMemoryException: Out of memory.]
   System.Drawing.Graphics.CheckErrorStatus(Int32 status) +1146420
   System.Drawing.Graphics.DrawImage(Image image, Rectangle destRect, Int32 srcX, Int32 srcY, Int32 srcWidth, Int32 srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttrs, DrawImageAbort callback, IntPtr callbackData) +256
   System.Drawing.Graphics.DrawImage(Image image, Rectangle destRect, Int32 srcX, Int32 srcY, Int32 srcWidth, Int32 srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttr) +48

这是调整大小,我得到异常:

public Bitmap ResizeImage(Image image, int width, int height)
        
          var newWidth = (int)(imageWidth * ratio) < 210 ? 210 : (int)(imageWidth * ratio);
          var newHeight = (int)(imageHeight * ratio) < 210 ? 210 : (int)(imageHeight * ratio);
            //Image resize logic

            var destRect = new Rectangle(0, 0, newWidth, newHeight);
            var destImage = new Bitmap(newWidth, newHeight);
            destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

            using (var graphics = Graphics.FromImage(destImage))
            
                graphics.CompositingMode = CompositingMode.SourceCopy;
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

                using (var wrapMode = new ImageAttributes())
                
                    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                    /*Here I get error*/graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel,
                        wrapMode);
                
            

            return destImage;
        

这里上传图片:

private async Task<short> UploadImage(string title, HttpPostedFileBase file, short dimensionWidth,
            short dimensionHeight)
        
            var blockBlob = CloudBlobContainer.GetBlockBlobReference(title);
            var jpgInfo = ImageCodecInfo.GetImageEncoders().First(codecInfo => codecInfo.MimeType == "image/jpeg");
            using (var image = Image.FromStream(file.InputStream, true, true))
            
                using (var stream = new MemoryStream())
                using (var encParams = new EncoderParameters(1))
                
                    encParams.Param[0] = new EncoderParameter(Encoder.Quality, 60L);
                    if (image.Width > dimensionWidth && image.Height > dimensionHeight)
                        using (Bitmap bitMapImage = ResizeImage(image, dimensionWidth, dimensionHeight))
                        
                            bitMapImage.Save(stream, jpgInfo, encParams);
                        
                    else
                    
                        image.Save(stream, jpgInfo, encParams);
                    
                    stream.Position = 0;
                    await blockBlob.UploadFromStreamAsync(stream);
                    blockBlob.Properties.CacheControl = "public, max-age=864000";
                    blockBlob.SetProperties();
                
            
            return (short)EnumData.EOpStatus.Success;
        

这里是主要功能:

        public async Task<string> UploadImages(string title, IEnumerable<HttpPostedFileBase> files, short fileCount)
        
            var fileIndex = 0;
            var imageCsv = String.Empty;
            var uploadTask = new Task<short>[fileCount * 2];
            foreach (var file in files)
            
                var fullTitle = title + "-" + Convert.ToString(fileIndex) + Path.GetExtension(file.FileName);
                uploadTask[fileIndex] = UploadImage(fullTitle, file, 1440, 900);
                uploadTask[fileIndex + 1] = UploadImage("thumb-" + fullTitle, file, 280, 280);
                imageCsv += String.IsNullOrEmpty(imageCsv) ? fullTitle : "," + fullTitle;
                /*await Task.WhenAll(uploadTask[fileIndex], uploadTask[fileIndex + 1]);*///Works fine in this case 

                fileIndex += 2;
            
            await Task.WhenAll(uploadTask);
            return imageCsv;
        

所以在一些上传后我得到一个错误

性能监视器在应用程序使用过程中似乎正常,我认为它正常

【问题讨论】:

您是否尝试过运行 Visual Studio 的内存分析工具? 这可能不是真正的 OOM 错误。 GDI+ 喜欢在您传递无效参数时返回这样的错误。 @Micky nope 现在尝试使用性能监视器 这提醒了我 - GDI 对象是有限制的,无论您拥有多少可用 RAM。 ***.com/questions/9723470/…。尽管您似乎在管理它们方面做得很好 使用 GDI(Bitmap 只是一个包装器)时,OutOfMemoryException 最好命名为 OutOfHandlesExceptionOutOfUnManagedMemoryException ,它通常与 CLR 分配的 # 个字节无关.它都是非托管内存和 GDI 句柄。但是,查看您的代码,我看不到任何会导致内存或句柄泄漏的明显错误。您在处理非常大的图像吗? 【参考方案1】:

工作完成后处理 sourceImage 和 destImage。

【讨论】:

您的答案对我来说看起来不错,但是您能否解释一下为什么会这样,并提供一个代码 sn-p 如何做到这一点?这将使这篇文章的未来访问者更容易找到他们的解决方案。谢谢。【参考方案2】:

我相信您确实在尝试在目标矩形之外绘制...如果您注意到您的代码实际上(在标记的行中)绘制 image.Widthimage.Height(原始尺寸),而不是比例- 应用尺寸...

【讨论】:

我认为您正在使用drawImage?我们应该从整个图像而不是一些固定的图像中绘制,这里请检查链接msdn.microsoft.com/en-us/library/ms142045(v=vs.110).aspx 出于好奇,试试graphics.DrawImage(image, destRect, 0, 0, 50, 50, GraphicsUnit.Pixel, wrapMode); 但是我建议的固定尺寸呢?同样的例外? 你尝试了固定大小,即 200、50 和正常大小

以上是关于异步上传和调整多个图像大小时出现内存不足异常的主要内容,如果未能解决你的问题,请参考以下文章

BitmapFactory.decodeFile 上的内存不足

电脑开机一会就出现内存不足,是啥原因

调整JVM内存大小

openMP:并行运行所有线程会导致内存不足异常

为啥在屏幕上绘制图像之前不会出现内存不足的问题?

位图 - 内存不足异常