[转载]网站性能优化:动态缩略图技术实现思路 – 旺 旺 – 博客园.
在网站开发过程中,大家都是如何解决多尺寸图片缩略图问题的呢?犹为典型的是电商网站,据了解,淘宝的图片缩略图是直接存储多张缩略图的方式, 以满足各种情况下使用,因为它有牛逼的开源+自主开发的海量图片存储架构作支撑。但是,我们在做网站时,并不可能直接搬牛逼的架构过来,就可以达到预期的 效果,况且各种成本投入也是有限的。所以一般性能优化的原则大都是这样:先考虑软件的优化,再考虑硬件的升级,当然土豪客户则除外。
很多网站可能没有对图片进行缩略图处理,上传时图片可能几百KB,在页面也是直接加载几百KB的图片大小,这样极为占用带宽,影响网站加载速 度。也有很多网站的做法可能也是直接根据前端页面所需求图片的尺寸,在上传时就处理生成相应尺寸的缩略图,但如果前端页面布局进行调整时,可能就得调整缩 略图生成的尺寸,之前生成的图片也有可能需要重新生成。之前我在一个网站项目时就遇到这样的问题,经过一系列地验证,最终是采用动态缩略图技术解决的,现 在整理出来给大家分享分享。
其实,原理很简单,通过高性能的图片压缩算法,在一般处理程序(HttpHandler)对图片进行压缩处理,图片路径则直接指向HttpHandler,将图片路径、需要压缩的宽高等参数传进去,实现动态压缩。
在网站目录下新建 ResizeImage.ashx 文件,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.IO; using System.Drawing.Imaging; using System.Drawing; namespace www.ideek.cn { /// <summary> /// 动态缩略图处理程序 /// 调用示例: <img runat="server" src="~/ResizeImage.ashx?src=/Upload/20140428/www_ideek_cn.jpg&width=128&height=128" /> /// </summary> public class ResizeImageHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string fileName = context.Server.UrlDecode(context.Request["src"]); if (string.IsNullOrEmpty(fileName)) { context.Response.Write("缺少参数src."); return; } fileName = Server.MapPath("~/" + fileName); Stream fileStream = null; try { string wStr = context.Request["width"]; string hStr = context.Request["height"]; int width = 0, height = 0; if (!string.IsNullOrEmpty(wStr) && !string.IsNullOrEmpty(hStr)) { int.TryParse(wStr, out width); int.TryParse(hStr, out height); } FileInfo fi = new FileInfo(fileName); if (!fi.Exists) { context.Response.Write("图片不存在."); return; } string contentType = getContentType(fi.Extension); context.Response.ContentType = contentType; //只能处理jpg及png图片格式,没有宽高参数不进行缩放处理 if (width > 0 && height > 0 && (contentType.Contains("jpeg") || contentType.Contains("png"))) { Image image = Image.FromFile(fi.FullName); int sWidth = image.Width, sHeight = image.Height; int nWidth = 0, nHeight = 0; if (sWidth > width || sHeight > height) { if (((double)sWidth / (double)sHeight) > ((double)width / (double)height)) { //以宽度为基准缩小 if (sWidth > width) { nWidth = width; nHeight = (int)(width * sHeight / (double)sWidth); } else { nWidth = sWidth; nHeight = sHeight; } } else { //以高度为基准缩小 if (sHeight > height) { nWidth = (int)(height * sWidth / (double)sHeight); nHeight = height; } else { nWidth = sWidth; nHeight = sHeight; } } Bitmap bitmap = new Bitmap(nWidth, nHeight, PixelFormat.Format32bppArgb); Graphics graphics = Graphics.FromImage(bitmap); graphics.Clear(Color.White); graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed; //平滑处理 graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; //缩放质量 graphics.DrawImage(image, new Rectangle(0, 0, nWidth, nHeight)); image.Dispose(); EncoderParameters parameters = new EncoderParameters(1); parameters.Param[0] = new EncoderParameter(Encoder.Quality, ((long)80)); //图片质量参数 fileStream = new MemoryStream(); bitmap.Save(fileStream, GetImageCodecInfo(contentType), parameters); using (MemoryStream ms = new MemoryStream()) { bitmap.Save(ms, GetImageCodecInfo(contentType), parameters); context.Response.OutputStream.Write(ms.GetBuffer(), 0, (int)ms.Length); } parameters.Dispose(); bitmap.Dispose(); return; } if (image != null) image.Dispose(); } else { fileStream = new FileStream(fi.FullName, FileMode.Open); byte[] bytes = new byte[(int)fileStream.Length]; fileStream.Read(bytes, 0, bytes.Length); fileStream.Close(); context.Response.BinaryWrite(bytes); } } catch (Exception ex) { context.Response.Write(ex.Message); } finally { if (fileStream != null) { fileStream.Close(); fileStream.Dispose(); } } System.GC.Collect(); } /// <summary> /// 获取文件下载类型 /// </summary> /// <param name="extension"></param> /// <returns></returns> private string getContentType(string extension) { string ct = string.Empty; switch (extension.ToLower()) { case ".jpg": ct = "image/jpeg"; break; case ".png": ct = "image/png"; break; case ".gif": ct = "image/gif"; break; case ".bmp": ct = "application/x-bmp"; break; default: ct = "image/jpeg"; break; } return ct; } //获得包含有关内置图像编码解码器的信息的ImageCodecInfo 对象. private ImageCodecInfo GetImageCodecInfo(string contentType) { ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders(); ImageCodecInfo jpegICI = null; for (int x = 0; x < arrayICI.Length; x++) { if (arrayICI[x].MimeType.Equals(contentType)) { jpegICI = arrayICI[x]; //设置JPEG编码 break; } } return jpegICI; } public bool IsReusable { get { return false; } } } }
按 Ctrl+C 复制代码
图片压缩算法中,有几个参数可以根据自己的需求进行调整,比如:SmoothingMode、InterpolationMode、Encoder.Quality,反正图片的大小与图片质量成正比,也跟性能成反比,具体参数用法,请移步MSDN查看,因此大家根据实际需求进行取舍了。
在页面需要调用地方,将img的src设置为ResizeImage.ashx,这跟图片验证码处理方式一样,如:
<img runat="server" src="~/ResizeImage.ashx?src=/Upload/20140428/www_ideek_cn.jpg&width=128&height=128" />
这样,一张图片可以在网站任意地方使用,图片经过压缩后传输,通过Google或Firefox浏览器开发者工具可以很明显地看出图片大小效果和加载速度都有非常明显的提升。
当然,可能很多人会问频繁的计算对服务器会产生性能影响,所以在实际使用过程中,可以根据自己的需求采用一些手段进行规避,比如引入缓存机制等,这些优化将会在后续文章中讲解。