using System; using System.Configuration; using System.Drawing; using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Text.RegularExpressions; using System.Web; using EW.EWorkpal.Core.Common.MongoProvider.MongoGridFSRepository.Models; using EW.EWorkpal.Core.System; using EW.EWorkpal.Services.FileManagerService.ImageProvider; namespace EW.EWorkpal.Modules.Picture { public class PictureWebHandler : ServiceBase, IHttpHandler { public void ProcessRequest(HttpContext context) { HttpResponse response = context.Response; HttpRequest request = context.Request; #region 客户端缓存 int clientCacheTime; //客户端缓存时间(秒) string clientCacheSecond = GetAppSetting("ClientCacheSecond", true); if (!int.TryParse(clientCacheSecond, out clientCacheTime)) clientCacheTime = 3600; string clearHeadImage = request.Headers["Clear-HeadImage"]; if (!string.IsNullOrEmpty(clearHeadImage)) { ClearClientCaching(context.Response); return; } string headerModified = request.Headers["If-Modified-Since"]; if (headerModified != null) { long nowTicks = DateTime.Now.Ticks; long modifiedTick = DateTime.Parse(request.Headers["If-Modified-Since"]).Ticks; long ticks = nowTicks - modifiedTick; if (TimeSpan.FromTicks(ticks).Seconds < clientCacheTime) { response.StatusCode = 304; // 这种方式会产生“此操作要求使用 IIS 集成管线模式。 ”的异常 //Response.Headers.Add("Content-Encoding", "gzip"); // Response.Headers.Add 和 Response.AddHeader 但是在MSDN中明确写出,这些都是为了兼容ASP,在.NET 3.5要求使用下面这种方式。 //这种方式不会出现异常 response.AppendHeader("Content-Encoding", "gzip"); response.StatusDescription = "Not Madified"; response.End(); return; } } #endregion string regex = GetAppSetting("UrlRegex"); string sourceRegex = GetAppSetting("SourceUrlRegex"); string headRegex = GetAppSetting("HeadImageUrlRegex"); string comLogoRegex = GetAppSetting("ComLogoUrlRegex"); string url = request.Url.AbsoluteUri; int regexIndex; if (Regex.IsMatch(url, headRegex, RegexOptions.IgnoreCase)) regexIndex = 3; else if (Regex.IsMatch(url, comLogoRegex, RegexOptions.IgnoreCase)) regexIndex = 4; else if (Regex.IsMatch(url, regex, RegexOptions.IgnoreCase)) regexIndex = 1; else if (Regex.IsMatch(url, sourceRegex, RegexOptions.IgnoreCase)) regexIndex = 2; else regexIndex = 0; if (regexIndex == 0) { Error404(response); return; } #region 用户头像 if (regexIndex == 3) { var regResult = Regex.Match(url, @"d+/d+").Value; var str = regResult.Split('/'); if (str.Length < 2) { Error404(response); return; } int id, size; if (!int.TryParse(str[0], out id)) { Error404(response); return; } if (!int.TryParse(str[1], out size)) { Error404(response); return; } if (size == 0) { size = 50; } else if (size == 1) { size = 80; } else { size = 150; } var srcPicBytes = GetService<IImageService>().GetHeadImage(id); if (srcPicBytes != null) { Stream stream = GetHighHeadImage(srcPicBytes, size); long dataToRead = stream.Length; byte[] bytes = ((MemoryStream)stream).GetBuffer(); response.Buffer = true; response.ContentType = "image/jpeg"; while (dataToRead > 0) { if (response.IsClientConnected) { int length = bytes.Length; byte[] buffer = bytes; response.OutputStream.Write(buffer, 0, length); response.Flush(); dataToRead = dataToRead - length; } else { dataToRead = -1; } } SetClientCaching(response, DateTime.Now, clientCacheTime); } else { //if (requesthost != picturehost)//盗链,返回提示图片 ////{ //// context.Response.WriteFile("/Img/linknotice/ImageForbiden.jpg"); ////} context.Response.ClearContent(); context.Response.WriteFile("/img/headimg/noavatar_" + size + "_" + size + ".jpg"); context.Response.ContentType = "image/jpeg"; context.Response.End(); return; } // context.Response.ClearContent(); // context.Response.ContentType = "image/jpeg"; context.Response.End(); return; } #endregion #region 企业Logo if (regexIndex == 4) { var regResult = Regex.Match(url, @"ent/d+").Value; var str = regResult.Split('/'); if (str.Length < 2) { Error404(response); return; } int size; if (!int.TryParse(str[1], out size)) { Error404(response); return; } if (size == 0) { size = 50; } else if (size == 1) { size = 100; } else { size = 150; } string fileName = regexIndex == 1 ? Regex.Match(url, regex, RegexOptions.IgnoreCase).Groups[1].Value : Regex.Match(url, sourceRegex, RegexOptions.IgnoreCase).Groups[1].Value; MongoFileInfo options; var srcPicBytes = GetService<IImageService>().Get(fileName, out options); if (srcPicBytes != null) { Stream stream = GetHighHeadImage(srcPicBytes, size); long dataToRead = stream.Length; byte[] bytes = ((MemoryStream)stream).GetBuffer(); response.Buffer = true; response.ContentType = "image/jpeg"; while (dataToRead > 0) { if (response.IsClientConnected) { int length = bytes.Length; byte[] buffer = bytes; response.OutputStream.Write(buffer, 0, length); response.Flush(); dataToRead = dataToRead - length; } else { dataToRead = -1; } } SetClientCaching(response, DateTime.Now, clientCacheTime); } else { context.Response.ClearContent(); context.Response.WriteFile(string.Format("/img/comlogo/{0}x{0}.jpg",size)); context.Response.ContentType = "image/jpeg"; context.Response.End(); return; } // context.Response.ClearContent(); // context.Response.ContentType = "image/jpeg"; context.Response.End(); return; } #endregion #region 读取原图 if (regexIndex == 1 || regexIndex == 2) { string fileName = regexIndex == 1 ? Regex.Match(url, regex, RegexOptions.IgnoreCase).Groups[1].Value : Regex.Match(url, sourceRegex, RegexOptions.IgnoreCase).Groups[1].Value; MongoFileInfo options; var srcPicBytes = GetService<IImageService>().Get(fileName, out options); if (srcPicBytes == null || srcPicBytes.Length <= 0) { Error404(response); return; } var srcPicStream = new MemoryStream(srcPicBytes); Image srcImage = Image.FromStream(srcPicStream); Image destImage = null; if (regexIndex == 1) { int destWidth = int.Parse(Regex.Match(url, regex, RegexOptions.IgnoreCase).Groups[2].Value); int destHeight = int.Parse(Regex.Match(url, regex, RegexOptions.IgnoreCase).Groups[3].Value); string zoomType = Regex.Match(url, regex, RegexOptions.IgnoreCase).Groups[4].Value; bool zoomCut = zoomType.ToUpper() == "-C"; bool zoomFit = zoomType.ToUpper() == "-F"; //缩略 destImage = ZoomPic.Zoom(srcImage, new ZoomParam { Width = destWidth, Height = destHeight, Type = zoomCut ? EnumZoomType.Cut : zoomFit ? EnumZoomType.Fit : EnumZoomType.Cut }); } else { //返回原图 destImage = srcImage; } #region 输出 Stream stream = new MemoryStream(); try { if (destImage != null) destImage.Save(stream, ImageFormat.Jpeg); long dataToRead = stream.Length; byte[] bytes = ((MemoryStream)stream).GetBuffer(); SetClientCaching(response, DateTime.Now, clientCacheTime); response.Buffer = true; response.ContentType = "image/jpeg"; while (dataToRead > 0) { if (response.IsClientConnected) { int length = bytes.Length; byte[] buffer = bytes; response.OutputStream.Write(buffer, 0, length); response.Flush(); dataToRead = dataToRead - length; } else { dataToRead = -1; } } } catch (Exception e) { ClearClientCaching(response); throw new Exception(e.Message); } finally { srcImage.Dispose(); srcPicStream.Dispose(); if (destImage != null) destImage.Dispose(); stream.Dispose(); } #endregion } #endregion } #region private methods private static string GetAppSetting(string key, bool allowNull = false) { if (ConfigurationManager.AppSettings[key] == null) { if (allowNull) return ""; throw new ConfigurationErrorsException(string.Format("找不到appSettings配置,配置项{0}", key)); } return ConfigurationManager.AppSettings[key]; } private void SetClientCaching(HttpResponse response, DateTime lastModified, int clientCacheTime) { response.ContentType = "image/jpeg"; //将ETag Http标头设置为制定字符串 response.Cache.SetETag(lastModified.Ticks.ToString(CultureInfo.InvariantCulture)); //将Last_Modified Http标头设置为提供的System.DateTime值 response.Cache.SetLastModified(lastModified); //将Cache-Control 标头设置为System.Web.HttpCacheability值之一 response.Cache.SetCacheability(HttpCacheability.Public); //基于制定的时间跨度设置Cache-Control: max-age HTTP标头 response.Cache.SetMaxAge(new TimeSpan(0, 0, 0, clientCacheTime)); //将缓存过期从绝对时间设置为可调时间 response.Cache.SetSlidingExpiration(true); } private void ClearClientCaching(HttpResponse response) { response.Cache.SetExpires(DateTime.Now); } private MemoryStream GetHeadImage(Byte[] file, int size) { size = size > 150 ? 150 : size; MemoryStream stream = new MemoryStream(file); Bitmap bmp = new Bitmap(Image.FromStream(stream)); Image image = bmp.GetThumbnailImage(size, size, null, IntPtr.Zero); MemoryStream ms = new MemoryStream(); image.Save(ms, ImageFormat.Jpeg); return ms; } private MemoryStream GetHighHeadImage(Byte[] file, int size) { MemoryStream ms = new MemoryStream(); Image originalImage = Image.FromStream(new MemoryStream(file)); //新建一个bmp图片 Image bitmap = new Bitmap(size, size); //新建一个画板 Graphics g = Graphics.FromImage(bitmap); //设置高质量插值法 g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; //设置高质量,低速度呈现平滑程度 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; //清空画布并以透明背景色填充 g.Clear(Color.Transparent); //在指定位置并且按指定大小绘制原图片的指定部分 g.DrawImage(originalImage, new Rectangle(0, 0, size, size), new Rectangle(0, 0, originalImage.Width, originalImage.Height), GraphicsUnit.Pixel); try { //以jpg格式保存缩略图 bitmap.Save(ms, ImageFormat.Png); } catch (Exception e) { throw e; } finally { originalImage.Dispose(); bitmap.Dispose(); g.Dispose(); } return ms; } #endregion /// <summary> /// 返回404 /// </summary> private static void Error404(HttpResponse response) { response.StatusCode = 404; response.Flush(); response.End(); } public bool IsReusable { get { return false; } } } }