使用ASP.NET创建缩略图的"最佳"方法是什么?
故事:用户上载将添加到相册的图像。作为上传过程的一部分,我们需要A)将图像存储在Web服务器的硬盘驱动器上,以及B)将图像的缩略图存储在Web服务器的硬盘驱动器上。
此处的"最佳"定义为
- 相对容易实现,理解和维护
- 产生质量合理的缩略图
性能和高质量缩略图是次要的。
解决方案
回答
我想最好的解决方案是使用.NET Image类中的GetThumbnailImage。
// Example in C#, should be quite alike in ASP.NET // Assuming filename as the uploaded file using ( Image bigImage = new Bitmap( filename ) ) { // Algorithm simplified for purpose of example. int height = bigImage.Height / 10; int width = bigImage.Width / 10; // Now create a thumbnail using ( Image smallImage = image.GetThumbnailImage( width, height, new Image.GetThumbnailImageAbort(Abort), IntPtr.Zero) ) { smallImage.Save("thumbnail.jpg", ImageFormat.Jpeg); } }
回答
这是VB.NET中图像类的扩展方法
Imports System.Runtime.CompilerServices Namespace Extensions ''' <summary> ''' Extensions for the Image class. ''' </summary> ''' <remarks>Several usefull extensions for the image class.</remarks> Public Module ImageExtensions ''' <summary> ''' Extends the image class so that it is easier to get a thumbnail from an image ''' </summary> ''' <param name="Input">Th image that is inputted, not really a parameter</param> ''' <param name="MaximumSize">The maximumsize the thumbnail must be if keepaspectratio is set to true then the highest number of width or height is used and the other is calculated accordingly. </param> ''' <param name="KeepAspectRatio">If set false width and height will be the same else the highest number of width or height is used and the other is calculated accordingly.</param> ''' <returns>A thumbnail as image.</returns> ''' <remarks> ''' <example>Can be used as such. ''' <code> ''' Dim _NewImage as Image ''' Dim _Graphics As Graphics ''' _Image = New Bitmap(100, 100) ''' _Graphics = Graphics.FromImage(_Image) ''' _Graphics.FillRectangle(Brushes.Blue, New Rectangle(0, 0, 100, 100)) ''' _Graphics.DrawLine(Pens.Black, 10, 0, 10, 100) ''' Assert.IsNotNull(_Image) ''' _NewImage = _Image.ToThumbnail(10) ''' </code> ''' </example> ''' </remarks> <Extension()> _ Public Function ToThumbnail(ByVal Input As Image, ByVal MaximumSize As Integer, Optional ByVal KeepAspectRatio As Boolean = True) As Image Dim ReturnImage As Image Dim _Callback As Image.GetThumbnailImageAbort = Nothing Dim _OriginalHeight As Double Dim _OriginalWidth As Double Dim _NewHeight As Double Dim _NewWidth As Double Dim _NormalImage As Image Dim _Graphics As Graphics _NormalImage = New Bitmap(Input.Width, Input.Height) _Graphics = Graphics.FromImage(_NormalImage) _Graphics.DrawImage(Input, 0, 0, Input.Width, Input.Height) _OriginalHeight = _NormalImage.Height _OriginalWidth = _NormalImage.Width If KeepAspectRatio = True Then If _OriginalHeight > _OriginalWidth Then If _OriginalHeight > MaximumSize Then _NewHeight = MaximumSize _NewWidth = _OriginalWidth / _OriginalHeight * MaximumSize Else _NewHeight = _OriginalHeight _NewWidth = _OriginalWidth End If Else If _OriginalWidth > MaximumSize Then _NewWidth = MaximumSize _NewHeight = _OriginalHeight / _OriginalWidth * MaximumSize Else _NewHeight = _OriginalHeight _NewWidth = _OriginalWidth End If End If Else _NewHeight = MaximumSize _NewWidth = MaximumSize End If ReturnImage = _ _NormalImage.GetThumbnailImage(Convert.ToInt32(_NewWidth), Convert.ToInt32(_NewHeight), _Callback, _ IntPtr.Zero) _NormalImage.Dispose() _NormalImage = Nothing _Graphics.Dispose() _Graphics = Nothing _Callback = Nothing Return ReturnImage End Function End Module End Namespace
抱歉,代码标签不喜欢vb.net代码。
回答
我们可以使用Image.GetThumbnailImage函数为我们完成此操作。
http://msdn.microsoft.com/zh-cn/library/system.drawing.image.getthumbnailimage.aspx(.NET 3.5)
http://msdn.microsoft.com/zh-cn/library/system.drawing.image.getthumbnailimage(VS.80).aspx(.NET 2.0)
public bool ThumbnailCallback() { return false; } public void Example_GetThumb(PaintEventArgs e) { Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback); Bitmap myBitmap = new Bitmap("Climber.jpg"); Image myThumbnail = myBitmap.GetThumbnailImage(40, 40, myCallback, IntPtr.Zero); e.Graphics.DrawImage(myThumbnail, 150, 75); }
回答
GetThumbnailImage可以使用,但是如果我们想要更好的质量,则可以为BitMap类指定图像选项,然后将加载的图像保存到该位置。这是一些示例代码:
Image photo; // your uploaded image Bitmap bmp = new Bitmap(resizeToWidth, resizeToHeight); graphic = Graphics.FromImage(bmp); graphic.InterpolationMode = InterpolationMode.HighQualityBicubic; graphic.SmoothingMode = SmoothingMode.HighQuality; graphic.PixelOffsetMode = PixelOffsetMode.HighQuality; graphic.CompositingQuality = CompositingQuality.HighQuality; graphic.DrawImage(photo, 0, 0, resizeToWidth, resizeToHeight); imageToSave = bmp;
这提供了比GetImageThumbnail开箱即用的更好的质量
回答
避免使用GetThumbnailImage,它将提供非常不可预测的结果,因为即使嵌入式缩略图的大小完全错误,它也会尝试使用嵌入式JPEG缩略图(如果可用)。 DrawImage()是一个更好的解决方案。
将位图包装在using {}子句中,我们不希望泄漏的句柄在周围漂浮...
另外,我们需要将Jpeg编码质量设置为90,这是GDI +似乎最出色的地方:
System.Drawing.Imaging.ImageCodecInfo[] info = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders(); System.Drawing.Imaging.EncoderParameters encoderParameters; encoderParameters = new System.Drawing.Imaging.EncoderParameters(1); encoderParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 90L); thumb.Save(ms, info[1], encoderParameters);
回答
使用上面的示例以及其他几个地方的示例,这里提供了一个简单的函数(感谢Nathanael Jones和其他示例)。
using System.Drawing; using System.Drawing.Drawing2D; using System.IO; public static void ResizeImage(string FileNameInput, string FileNameOutput, double ResizeHeight, double ResizeWidth, ImageFormat OutputFormat) { using (System.Drawing.Image photo = new Bitmap(FileNameInput)) { double aspectRatio = (double)photo.Width / photo.Height; double boxRatio = ResizeWidth / ResizeHeight; double scaleFactor = 0; if (photo.Width < ResizeWidth && photo.Height < ResizeHeight) { // keep the image the same size since it is already smaller than our max width/height scaleFactor = 1.0; } else { if (boxRatio > aspectRatio) scaleFactor = ResizeHeight / photo.Height; else scaleFactor = ResizeWidth / photo.Width; } int newWidth = (int)(photo.Width * scaleFactor); int newHeight = (int)(photo.Height * scaleFactor); using (Bitmap bmp = new Bitmap(newWidth, newHeight)) { using (Graphics g = Graphics.FromImage(bmp)) { g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.SmoothingMode = SmoothingMode.HighQuality; g.CompositingQuality = CompositingQuality.HighQuality; g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.DrawImage(photo, 0, 0, newWidth, newHeight); if (ImageFormat.Png.Equals(OutputFormat)) { bmp.Save(FileNameOutput, OutputFormat); } else if (ImageFormat.Jpeg.Equals(OutputFormat)) { ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders(); EncoderParameters encoderParameters; using (encoderParameters = new System.Drawing.Imaging.EncoderParameters(1)) { // use jpeg info[1] and set quality to 90 encoderParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 90L); bmp.Save(FileNameOutput, info[1], encoderParameters); } } } } } }