Resizeovaci image HttpHandler

Kód je downgrade produkčnej verzie resp. staršia záplatová verzia. Chýba riešenie caching-u, či už z hľadiska HTTP protokolu, ako i z hľadiska nutnosti znovagenerovať thumbnails - čo je neefektívne v prípade, že niektoré images sú častejšie zobrazované. Pracuje len s jpg, infrastruktúra ale umožnuje ľahko pridať možnosť špecifikovať formát alebo upraviť handler na iný formát. Z hľadiska zraniteľnosti aplikácie neodporúčam všeobecný interface handlera (url parametre) umožnujúci zadať ľubovolné parametre a v kontexte portieb konkrétneho druhu thumbnail validovať rozumné zosahy parametrov.

Použitie:

 <img src="img.ashx?300;300;foo;70"/>

kde src -> pattern.ashx?width;height;nazov[;quality]

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;
using AspNetSK.Web.UI;
using AspNetSK.Drawing;
namespace AspNetSK.Web
{
public abstract class ImageResizeHttpHandlerBase : IHttpHandler
{
protected abstract string RootPath { get; }
#region IHttpHandler Members
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
HttpRequest request = context.Request;
HttpResponse response = context.Response;
string imgParams = request.Url.Query;
if(string.IsNullOrEmpty(imgParams))
{
HandleError("Invalid parameters");
response.End();
}
imgParams = imgParams.Replace("?", "");
string[] arrImgParams = imgParams.Split(new char[]{';'});
if (arrImgParams.Length < 3)
{
HandleError("Invalid parameters");
response.End();
}
int width, height;
short quality = 70;
if(!int.TryParse(arrImgParams[0], out width) || width < 1 || width > 1200)
{
HandleError("Invalid width.");
return;
}
if (!int.TryParse(arrImgParams[1], out height) || height < 1 || height > 1200)
{
HandleError("Invalid height.");
return;
}

string path = context.Server.MapPath(RootPath + "/" + arrImgParams[2] + ".jpg");
if (!File.Exists(path))
{
WriteStatusAndTerminate(404);
return;
}

if (arrImgParams.Length > 3)
{
if (!short.TryParse(arrImgParams[3], out quality) || quality < 1 || quality > 100)
{
HandleError("Invalid quality.");
return;
}
}
Bitmap image = null, tmbImage = null;
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
image = (Bitmap)Bitmap.FromStream(fileStream);
try
{
tmbImage = ImageHelper.GetThumbNail(
image,
new Size(width, height),
ScaleType.Clipp);

ImageHelper.SaveCompressedJpg(
tmbImage,
response.OutputStream,
quality);

}
catch(Exception ex)
{
HandleError(ex);
}
finally
{
if(image != null)
image.Dispose();
if(tmbImage != null)
image.Dispose();
}
}
protected virtual void HandleError(Exception ex)
{
#if DEBUG
throw ex;
#else
HandleError("Error processing image", 500);
#endif
}
protected virtual void HandleError(string message)
{
HandleError(message, 500);
}
protected virtual void HandleError(string message, int status)
{
#if DEBUG
throw new Exception(message);
#else
WriteStatusAndTerminate(status, message);
#endif
}
protected virtual void WriteStatusAndTerminate(int status)
{
WriteStatusAndTerminate(status, null);
}
protected virtual void WriteStatusAndTerminate(int status, string message)
{
HttpResponse response= HttpContext.Current.Response;
response.ClearHeaders();
response.ClearContent();
response.StatusCode = status;
if(message != null)
response.StatusDescription = message;
response.End();
}
#endregion
}
}
Treba urobit override pre kazdy typ handleru:
namespace AspNetSK.Web
{
public class ArticleListTmbHttpHandler : ImageResizeHttpHandlerBase
{
protected override string RootPath
{
get { return "~/img/art/"; }
}
}
}
 Web.config:
<add path="img.ashx" verb="GET,HEAD" validate="false" type="AspNetSK.Web.ImageHttpHandler" /> 
A resize helper:
 
using System;
using System.IO;
using System.Data;
using System.Configuration;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace AspNetSK.Drawing
{
public enum ScaleType
{
Clipp,
ScaleToFit,
ScaleIntoBounds
}

public class ImageHelper
{
public static Bitmap GetThumbNail(Bitmap image, Size tmbSize, ScaleType scaleType)
{
int iSourceWidth = image.Width;
int iSourceHeight = image.Height;
if (scaleType == ScaleType.Clipp || scaleType == ScaleType.ScaleIntoBounds)
{
SizeF tmbSizeF = (SizeF)tmbSize;
SizeF origSizeF = (SizeF)image.Size;
float widthRatio = origSizeF.Width / tmbSizeF.Width;
float heightRatio = origSizeF.Height / tmbSizeF.Height;

if (scaleType == ScaleType.Clipp)
{
if (widthRatio < heightRatio)
iSourceHeight = (int)Math.Round((double)(tmbSizeF.Height * widthRatio));
else
iSourceWidth = (int)Math.Round((double)(tmbSizeF.Width * heightRatio));
}
else
{
if (widthRatio > heightRatio)
tmbSize.Height = (int)Math.Round((double)(origSizeF.Height / widthRatio));
else
tmbSize.Width = (int)Math.Round((double)(origSizeF.Width / heightRatio));
}
}

Bitmap thumbnail = new Bitmap(tmbSize.Width, tmbSize.Height, PixelFormat.Format24bppRgb);

using (Graphics graphics = Graphics.FromImage(thumbnail))
{
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle oDestRectangle = new Rectangle(0, 0, tmbSize.Width, tmbSize.Height);
Rectangle oSrcRectangle = new Rectangle(0, 0, iSourceWidth, iSourceHeight);
graphics.DrawImage(image, oDestRectangle, oSrcRectangle, GraphicsUnit.Pixel);
}
return thumbnail;
}


public static void SaveCompressedJpg(Image img, string fileName, short compressLevel)
{
using (EncoderParameters codecParams = new EncoderParameters(1))
using (EncoderParameter param = new EncoderParameter(Encoder.Quality, (long)compressLevel))
{
codecParams.Param[0] = param;
ImageCodecInfo codecInfo = FindEncoder(ImageFormat.Jpeg);
img.Save(fileName, codecInfo, codecParams);
}
}

public static void SaveCompressedJpg(Image img, Stream stream, short compressLevel)
{
using (EncoderParameters codecParams = new EncoderParameters(1))
using (EncoderParameter param = new EncoderParameter(Encoder.Quality, (long)compressLevel))
{
codecParams.Param[0] = param;
ImageCodecInfo codecInfo = FindEncoder(ImageFormat.Jpeg);
img.Save(stream, codecInfo, codecParams);
}
}


private static ImageCodecInfo FindEncoder(ImageFormat eImgFormat)
{
ImageCodecInfo[] arrCodecInfo = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo[] arrCodecInfo2 = arrCodecInfo;
for (int i = 0; i < arrCodecInfo2.Length; i++)
{
ImageCodecInfo oCodecInfo = arrCodecInfo2[i];
if (oCodecInfo.FormatID.Equals(eImgFormat.Guid))
return oCodecInfo;
}
return null;
}
}
}
Bookmark and Share

Komentáre

# Dušan said:

Oooooo, nice. Vďaka :) Vyskúšam :)

Friday, March 13, 2009 9:54 AM
# Petr said:

Používám ImageGen

www.percipientstudios.com/.../overview.aspx

Monday, March 16, 2009 8:45 AM
# T said:

Vdaka za linku Petre, urcite by som uvital hotovy komponent, ale skor na vizualnu pracu s obrazkom cez javascript.(po uploade, crop, resize, filtre etc. ) . Bohuzial tu zaujimavejsiu cast ten component zvlada len v platenej verzii. Btw. hento hore je hlavne edukacny piece of code ;-)  

Monday, March 16, 2009 9:21 AM
# 345353 said:

Vies, ze "image" je po slovensky "obrazok"?

Sunday, May 10, 2009 10:41 PM
# T said:

@Petr:

vdaka Petr...crop ale podla toho, co pisu zvlada az platena(co niekoho trapit nemusi, takze mu component posluzi)

@345353: fakt nie

Tuesday, May 19, 2009 2:36 PM
Prihlásiť | Registrovať | Pomoc