使用IHttphandler为图片添加水印和防盗链功能

作者:陆金龙    发表时间:2014-10-02 13:38   


用IHttphandler实现为图片添加水印是Aspnet中很经典的做法了,这方面的介绍网上很多,不过还是会有一些细节方面的问题值得注意,可能会因为某些细节的处理不好而出不来效果。对于开发而言,理解了、懂了不能算真正掌握,只有写出来,调试通过,部署成功,运行没有问题才算OK。

本文将图片添加水印功能的技术实现、web.config配置、IIS配置等按操作步骤梳理出来,对于可能遇到的问题进行了分析。既方便初学者按部就班的演练,也为有经验的开发者在排查问题时提供参考。另外,顺带加上了防盗链功能,对添加文字水印和Logo水印的情况也相应给出了两种图片处理方案。

下面,就按步骤开始吧!

步骤一:新建一个类WaterMark,实现IHttphandler接口

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Linq;

using System.Web;

public class WaterMark : IHttpHandler

{

    public bool IsReusable

    {

        get { throw new NotImplementedException(); }

    }

 

    public void ProcessRequest(HttpContext context)

    {

        throw new NotImplementedException();

    }

}

检查文件的相关属性,复制到输出目录选择“不复制”,生成操作为“编译”,如下:

步骤二:重写WaterMark类的方法,实现相应的功能

   public void ProcessRequest(HttpContext context)

   {

        context.Response.ContentType = "image/jpeg";

        //盗链下载(输出防盗链图片)

        Uri urlRef = context.Request.UrlReferrer;

        Uri url = context.Request.Url;

        if (!IsSameDomain(urlRef, url))

        {

            string daolianPath = context.Request.MapPath("~/Images/daolian.jpg");

            context.Response.WriteFile(daolianPath);

        }

        //正常下载(在图片上添加水印文字)

        else

        {

            //获取提交的图片路径

            string path = context.Request.RawUrl;

            path = context.Request.MapPath(path);

            //画板

            using (Bitmap bitmp = new Bitmap(path))

            {

                //画笔

                using (Graphics g = Graphics.FromImage(bitmp))

                {

                    Font font = new System.Drawing.Font("微软雅黑", 14, (System.Drawing.FontStyle.Italic));

                    System.Drawing.Drawing2D.LinearGradientBrush brush = new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(0, 0, bitmp.Width, bitmp.Height), Color.White, Color.White, 1.2f, true);

                    g.DrawString("iprogram.com.cn", font, brush, bitmp.Width - 165, bitmp.Height - 32);

                }

                bitmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);

            }

        }   

    }

 

    //UriComponents.HostAndPort,比较两个路径的域名

    //UriFormat.SafeUnescaped,具有保留意义的字符仍进行转义

    //StringComparison.CurrentCultureIgnoreCase,使用区域敏感排序规则,当前区域比较,忽略大小写

    bool IsSameDomain(Uri url1, Uri url2)

    {

        return Uri.Compare(url1, url2, UriComponents.HostAndPort, UriFormat.SafeUnescaped, StringComparison.CurrentCultureIgnoreCase) == 0;

    }

    public bool IsReusable

    {

        //有并发同时存在的情况下,当前的handle调用了某个独占的线程(进程),那么就需要设置为false。比如说,handle独占写入某个文本文件(所有的并发都需要写入这个),那就需要设置为false了。

        //如果handle没有这种非安全的情况,就可以直接设置为true。通常大多数项目都设置为true的

        get { return false; }

    }

步骤三:web.config配置

(1)带程序集名的配置

<system.web>

<httpHandlers>

      <add path="Upload/Images////////.jpg" type="WaterMark" verb="*"  />

    <add path="Upload/Images////////.png" type="WaterMark" verb="*"  />

</httpHandlers>

</system.web>

以上配置可能出现以下报错:

 

配置错误 

说明: 在处理向该请求提供服务所需的配置文件时出错。请检查下面的特定错误详细信息并适当地修改配置文件。 
分析器错误消息: 类型“WaterMark”不明确: 它可能来自程序集“C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\3f8eda58\df96fc10\App_Code.kgqjr5m3.DLL”或程序集“I:\i编程网\IProgram.CMS\IProgram.CMS.UI.Portal\bin\IProgram.CMS.UI.Portal.DLL”。请在类型名称中显式指定程序集。

(2)带程序集名的配置

如果报错提示有多个程序集包含该类,要求显示指定程序集,可按如下方式进行配置:

<system.web>

<httpHandlers>

      <add path="Upload/Images////////.jpg" type="WaterMark,IProgram.CMS.UI.Portal" verb="*"  />

    <add path="Upload/Images////////.png" type="WaterMark,IProgram.CMS.UI.Portal" verb="*"  />

</httpHandlers>

</system.web>

步骤四:IIS配置

经过以上配置,运行VS调试,可看到图片上都打上了“iprogram.com.cn”文字的水印。效果如下图所示:

但是发布到IIS后,运行发现该功能没有生效,图片上并没有出现水印。这是由于,在默认情况下,jpg、png等这些图片资源的属于静态资源,IIS会将资源找到直接响应给客户端,并不经过aspx程序的处理再输出。

我们知道IIS在接到请求后会查询应用程序扩展的映射表,以决定哪些类型的请求交给什么应用程序处理,对于映射表中没有配置的静态资源会直接输出。要将图片请求交给aspnet服务端处理,只要在映射表中为其添加一条记录,关联aspnet_isapi.dll即可。具体配置步骤如下:

(1)找到网站->右键打开属性窗口

(2)主目录->点击“配置”进行设置

(3)映射->添加

     点击“浏览”到.netframework 版本号文件夹下找到aspnet_isapi.dll,扩展名写图片的后缀名,如jpg,到此配置完成。(如果有多种后缀名的如png,需要为png添加一条新的映射记录。)

附:为图片添加Logo水印

有时候可能希望给图片打上一个自己公司的Logo做为谁赢,而不是文字水印。可以将图片处理这块代码改成如下的代码:

 

        //获取提交的图片路径

        string path = context.Request.RawUrl;

        path = context.Request.MapPath(path);

        //水印图片的路径

        string logoPath = context.Request.MapPath("~Images/logo.png");

        //原始图片

        using (Image img = Image.FromFile(path))

        {

            //水印图片

            using (Image logoImg = Image.FromFile(logoPath))

            {

                using (Graphics g = Graphics.FromImage(img))

                {

                    g.DrawImage(logoImg, 0, 0, logoImg.Width, logoImg.Height);

 

                    img.Save(context.Response.OutputStream,

System.Drawing.Imaging.ImageFormat.Jpeg);

                }

            }

   }

    (全文完)

(转载本文请注明作者和出处 i 编程网-iprogram.com.cn ,请勿用于任何商业用途)