服务器上的缩略图 - GDI+ 中出现一般错误

Posted

技术标签:

【中文标题】服务器上的缩略图 - GDI+ 中出现一般错误【英文标题】:Thumbnails on Server - A generic error occurred in GDI+ 【发布时间】:2019-09-25 22:51:19 【问题描述】:

我们已经收到这个问题很长时间了,并尝试了很多来自网络的不同修复方法。到目前为止,没有任何效果。

问题:基本图像保存得很好,缩略图在保存时失败。在下面的解决方案之前,我尝试为所有图像(基本图像、600x600 和 300x300 拇指)创建单独的流,但这也不起作用。所有的流都是从同一个字节数组构造的。请记住这一点:这在开发环境、测试环境和 Test2 环境中运行良好,但在生产环境中无法运行。我检查了文件夹的所有设置/环境变量/权限,所有设置都与测试环境相同。

进来的路径如下:

基本路径:“~/images/imageUpl/” 缩略图添加。路径:“thumbM/” 缩略图添加。路径:“thumbS/” 图像名称结构:“X_YYYYMMDD_HHMMSS_XX.png”

路径都正确 - 因为它适用于 Dev/Test/Test2 环境。

非常感谢您对此的任何帮助!

编辑:到目前为止我们尝试了什么:

设置网络和 IISUser 的权限 为从原始源数据构建的每个图像使用单独的流 按照其他示例添加 thread.sleep(30+) 从调整大小的位图创建新的位图并保存 测试其目录是否在生产环境中出现问题的不同路径

编辑 2:作为参考,这是一个运行 .NET Framework 4.7.2 的 ASP.NET MVC5 Web 应用程序。

图像处理器类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Drawing.Drawing2D;
using Newtonsoft.Json;
using System.Threading;

namespace CMS_MVC.Classes

    public class ImageProcessor : IDisposable
    
        public enum PathType : int
        
            Relative = 1,
            Static = 2
        

        private Stream _ImageStream  get; set; 
        private HttpContext _Context  get; set; 
        public Image BaseImage  get; set; 
        private int _instanceId  get; set; 

        public ImageProcessor(int instanceId, Stream imageStream, HttpContext context)
        
            this._ImageStream = imageStream;
            this._Context = context;
            this._instanceId = instanceId;

            this.BaseImage = Image.FromStream(this._ImageStream);
        

        public ImageProcessor(int instanceId, byte[] imageByteArray, HttpContext context)
        
            this._Context = context;
            this._instanceId = instanceId;

            this._ImageStream = new MemoryStream(imageByteArray);
            this.BaseImage = Image.FromStream(this._ImageStream);
        

        public ImageProcessor(int instanceId, string imagePath, PathType pathType, HttpContext context)
        
            this._Context = context;
            this._instanceId = instanceId;

            if (pathType == PathType.Relative)
            
                this._ImageStream = new MemoryStream(File.ReadAllBytes(this._Context.Server.MapPath(imagePath)));
                this.BaseImage = Image.FromStream(this._ImageStream);
            
            else
            
                this._ImageStream = new MemoryStream(File.ReadAllBytes(imagePath));
                this.BaseImage = Image.FromStream(this._ImageStream);
            
        

        public Dictionary<string, bool> SaveImages(string baseImageSavePath, string imageName, Dictionary<string, Tuple<int, int>> thumbnails = null)
        
            Dictionary<string, bool> results = new Dictionary<string, bool>();
            string lastResult = "main";
            results.Add(lastResult, true);

            try
            
                this.BaseImage.Save(this._Context.Server.MapPath(Path.Combine(baseImageSavePath, imageName)), ImageFormat.Png);

                if (thumbnails != null)
                
                    foreach (var thumbnail in thumbnails)
                    
                        lastResult = thumbnail.Value.Item1.ToString() + "_" + thumbnail.Value.Item2.ToString();
                        results.Add(lastResult, true);

                        using (Bitmap thumbBitmap = this.ResizeImage(thumbnail.Value.Item1, thumbnail.Value.Item2))
                        
                            Thread.Sleep(50);
                            thumbBitmap.Save(this._Context.Server.MapPath(Path.Combine(baseImageSavePath + thumbnail.Key, imageName)), ImageFormat.Png);
                            Thread.Sleep(50);
                        
                    
                
            
            catch (Exception ex)
            
                results[lastResult] = false;

                // Log event
            

            return results;
        

        private Bitmap ResizeImage(int targetWidth, int targetHeight)
        
            Tuple<int, int> destSize = this.CalculateThumbnailSizeAspectRatio(targetWidth, targetHeight);

            var destRect = new Rectangle(0, 0, destSize.Item1, destSize.Item2);
            var destImage = new Bitmap(destSize.Item1, destSize.Item2);

            destImage.SetResolution(this.BaseImage.HorizontalResolution, this.BaseImage.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);
                    graphics.DrawImage(this.BaseImage, destRect, 0, 0, this.BaseImage.Width, this.BaseImage.Height, GraphicsUnit.Pixel, wrapMode);
                
            

            return destImage;
        

        private Tuple<int, int> CalculateThumbnailSizeAspectRatio(int targetWidth, int targetHeight)
        
            // Resize calculations
        

        public void Dispose()
        
            if (this._ImageStream != null) this._ImageStream.Dispose();
            if (this.BaseImage != null) this.BaseImage.Dispose();
        
    

【问题讨论】:

相关:***.com/q/56027141/60761 你should not use System.Drawing on a server “尝试了很多来自网络的不同修复” - 但您没有找到任何建议远离服务器上的 System.Drawing?基本上简单地说,GDI+ 用于用户界面,而 Windows 服务则没有。当从 Windows 服务(IIS 也是如此)使用 GDI+ 时,这会导致各种问题,因此您的 ASP.NET 代码在服务中运行。找到合适的替代品。 @CodeCaster 老实说,我从未见过或听说过这个警告,这是我第一次看到它。我还可以补充一点,当通过使用相同代码的手动图像上传器运行时,它可以在该生产框上运行。 Web 应用程序通常作为服务 (IIS) 运行或在服务 (IIS) 下运行。无论如何,ASP.NET 明确包含在警告中。 【参考方案1】:

来自documentation 网站:

System.Drawing 命名空间中的类不支持在 Windows 或 ASP.NET 服务中使用。尝试从这些应用程序类型之一中使用这些类可能会产生意想不到的问题,例如服务性能下降和运行时异常。

然后该站点会将您定向到 WIC,但这不是一个好主意。服务器也不支持 WPF 媒体类(但缺少关于此的同样明确的声明)。

各种替代方案出现了,这里是an older SO question。

最好的办法是寻找完全托管的解决方案,a.o.图像锐利。

我仍在寻找有关 System.Drawing.Common(一个 .net 核心库)的类似声明或见解。

【讨论】:

以上是关于服务器上的缩略图 - GDI+ 中出现一般错误的主要内容,如果未能解决你的问题,请参考以下文章

GDI+ 中出现一般错误 - 将位图保存到桌面

GDI+ 中发生一般性错误。 究竟是啥问题不是一般性的图片上传出错。

GDI+中发生一般性错误

识别缩略图加载

System.Drawing.Bitmap.GetHicon() 上的 GDI 对象泄漏

如何解决 winforms 错误“GDI+ 中发生一般错误。”?