(转)在网页上显示缩略图


Posted on March 10, 2007


网站上可能会有很多图片,比如产品图片等,而且他们可能大小不一,宽度和高度也不一定一样,有的很大有的很小。如果放在一张网页上,可能会破坏版面,但是如果强制让他们按照指定的宽度和高度显示,因为比例不同还会出现变形,显示效果很糟糕,还有最大的缺点是,文件尺寸丝毫没有变化,当图片很大的时候,用户想要看到图片,必须经过漫长等待下载图片,怎么办呢?

好,这里设计到了缩略图,就像Windows中的缩略图查看一样,你所看到的是从原图按照1:1比例缩小的图片,而且满足规定在指定宽度和高度的范围内显示(如果图片填不满,就用空白)。缩略图不是原图,而是利用原图实时按照指定大小生成的,他的好处就是你可以充分控制缩略图的质量,宽度高度,文件大小也在合理的范围内,省去漫长等待。

本文将讨论如何生成缩略图,举一反三,又可以派生许多用处,比如,自己写一个验证码控件等。

1、理解对图片的请求和流
    一般来说,我们用http://xxx/a.aspx对a.aspx网页请求。ASP.NET处理了网页以后,就把该网页的内容发送回浏览器。a.aspx的内容一般是含有超文本标记的文本文件流,这是谁都不会怀疑的。但是a.aspx不但能够返回这种非常平常的网页文本外,把它广义开来,它其实可以放回任何类型的流数据。而,我们只需要对Response对象进行操作即可改变输出流的内容。

    把图像文件看作是一个二进制流,我们试图从一个图像文件创建了他的流对象,并且将流写入到Response.OutputStream中,这样图像文件就被发给了请求的浏览器。但是浏览器还必须要识别出这是一个图像文件,因此,在发送这个流之前,将Response.ContentType更改成这种图像文件的MIME类型。浏览器在收到这个流之后,调用相关的应用程序,图像就被显示在了浏览器上。虽然实际地址还是aspx结尾。

    这样我们就能很好理解怎么去向用户发送标记。例如,在一张普通的网页中写标记img标记,使他的src指向a.aspx。浏览器在得到这张网页后,会处理img标记的内容,并向a.aspx发出请求,这是a.aspx作为图像流返回,浏览器就将图片显示出来。

2、生成缩略图
    有了上述技术基础,我们可以建立这样一个空的aspx网页,他通过接受传入的参数,生成缩略图的流,发送回浏览器。
    我们创建一个名叫GetThumbnail.aspx的文件,内容如下:

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="GetThumbnail.aspx.vb" Inherits="_51use.GetThumbnail"%>

这一句Page指令仅仅是告诉服务器,这个文件的隐藏类是_51use.GetThumbnail,而如果我们都不作任何操作的话,这个文件最终输出时空的。
接下来看一下他的隐藏类是如何写的,在这里我们使用的是B语言:

程序代码:

Public Class GetThumbnail
    Inherits System.Web.UI.Page

#Region " Web 窗体设计器生成的代码 "

    '该调用是 Web 窗体设计器所必需的。
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

    End Sub

    '注意: 以下占位符声明是 Web 窗体设计器所必需的。
    '不要删除或移动它。
    Private designerPlaceholderDeclaration As System.Object

    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        'CODEGEN: 此方法调用是 Web 窗体设计器所必需的
        '不要使用代码编辑器修改它。
        InitializeComponent()
    End Sub

#End Region

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        '在此处放置初始化页的用户代码
        '获取几个参数,用以生成缩略图片
        '设置一个默认参数生成BitMap
        Response.Clear()
        Try
            Dim originalFileName As String = Server.MapPath(Request("fn"))
            Dim thumbnailWidth As Integer = Request("tw")
            Dim thumbnailHeight As Integer = Request("th")

            Dim img As System.Drawing.Image = System.Drawing.Image.FromFile(originalFileName)
            Dim thisFormat As System.Drawing.Imaging.ImageFormat = img.RawFormat
            Dim newSize As System.Drawing.Size = Me.GetNewSize(thumbnailWidth, thumbnailHeight, img.Width, img.Height)
            Dim outBmp As New System.Drawing.Bitmap(thumbnailWidth, thumbnailHeight)
            Dim g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(outBmp)

            '设置画布的描绘质量
            g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
            g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
            g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
            g.Clear(System.Drawing.Color.FromArgb(255, 249, 255, 240))
            g.DrawImage(img, New Rectangle((thumbnailWidth - newSize.Width) / 2, (thumbnailHeight - newSize.Height) / 2, newSize.Width, newSize.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel)
            g.Dispose()

            If thisFormat.Equals(System.Drawing.Imaging.ImageFormat.Gif) Then
                Response.ContentType = "image/gif"
            Else
                Response.ContentType = "image/jpeg"
            End If

            '设置压缩质量
            Dim encoderParams As New System.Drawing.Imaging.EncoderParameters
            Dim quality(0) As Long
            quality(0) = 100
            Dim encoderParam As New System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality)
            encoderParams.Param(0) = encoderParam

            '获得包含有关内置图像编码解码器的信息的ImageCodecInfo 对象。
            Dim arrayICI() As System.Drawing.Imaging.ImageCodecInfo = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()
            Dim jpegICI As System.Drawing.Imaging.ImageCodecInfo = Nothing
            For fwd As Integer = 0 To arrayICI.Length - 1
                If arrayICI(fwd).FormatDescription.Equals("JPEG") Then
                    jpegICI = arrayICI(fwd)
                    Exit For
                End If
            Next

            If Not jpegICI Is Nothing Then
                outBmp.Save(Response.OutputStream, jpegICI, encoderParams)
            Else
                outBmp.Save(Response.OutputStream, thisFormat)
            End If
        Catch ex As Exception

        End Try
    End Sub

    Function GetNewSize(ByVal maxWidth As Integer, ByVal maxHeight As Integer, ByVal width As Integer, ByVal height As Integer) As System.Drawing.Size
        Dim w As Double = 0.0
        Dim h As Double = 0.0
        Dim sw As Double = Convert.ToDouble(width)
        Dim sh As Double = Convert.ToDouble(height)
        Dim mw As Double = Convert.ToDouble(maxWidth)
        Dim mh As Double = Convert.ToDouble(maxHeight)

        If sw < mw And sh < mh Then
            w = sw
            h = sh
        ElseIf (sw / sh) > (mw / mh) Then
            w = maxWidth
            h = (w * sh) / sw
        Else
            h = maxHeight
            w = (h * sw) / sh
        End If
        Return New System.Drawing.Size(Convert.ToInt32(w), Convert.ToInt32(h))
    End Function
End Class

我们在Page_Load事件处理函数中,先获取要生成缩略图的原始文件的路径,和缩略图的宽度高度。
然后设置了一个子函数GetNewSize用以计算真正缩略图的大小(为什么还要计算阿?因为缩略图的宽高比和原始图片的宽高比不一样,缩小的图片要保证比例,按照比例缩小的原始图片,是按照约束在指定缩略图宽高范围内的原则,填充不满的地方使用背景色填补空白。另外,原图比缩略图小的话,我们就不做放大,而是按原图输出,所以计算是必须的)。

我们从原始图像创建了他的Image对象,并获得它的格式等信息,稍后用得到。

接下来新建一个BitMap对象,并由新的BitMap对象创建画板。设置画笔质量为高,交错模式为高质量立方式,这些的目的是使用最好的质量描绘缩略图,否则图片缩小后信息丢失图片失真就不好看了。接着,用指定的宽度和高度将原始图像的Image对象“画”在新的画板上。

修改Response.ContentType,这一步是告诉浏览器发送回的流的MIME类型,这个内容包含在HTTP Header中发送给浏览器。

图像画好了,现在我们要将其压缩一下,因为位图对象是很大的,不利于传输。因此下面的操作,我们设定一些高质量的压缩参数,根据获得的ICI(图像编码解码器信息),使用BitMap的Save方法将图片保存在Response.OutputStream流中。

这样在浏览器看来,对该网页的请求,相当于对一个图片文件的请求,只不过图片是实时生成的,只需传递参数合法有效,即可得到该图片的缩略图。

3、使用缩略图
    使用缩略图就变得相对简单了,只需要在URL后附上参数fn表示原始文件的位置(相对于根目录),tw缩略图宽度,th缩略图高度,下列简单显示了在Repeater中使用的情景:

<asp:Repeater id=repWareList runat="server" DataSource="<%# dsWareList %>">
  <ItemTemplate>
    <div class="ItemContainer">
      <p><img src='../lib/GetThumbnail.aspx?fn=<%# Server.UrlEncode(Configurationsettings.AppSettings("WareImgPath") & Container.DataItem("WareImgPath"))%>&tw=120&th=120' width="120" height="120" ></p>
    </div>
  </ItemTemplate>
</asp:Repeater>

代码中蓝色部分就是对该页请求的URL,使用<%# Server.UrlEncode(Configurationsettings.AppSettings("WareImgPath") & Container.DataItem("WareImgPath"))%>语句从数据库获得路径和文件名适当构造成一个有效的请求路径。工作就算完成了。
后记
    本文中所述的缩略图生成法,使用的是流的概念,和文件系统也不沾边,因此这种方式可以跳过对文件系统权限检查,百分之百正确运行。当然,也可以借助文件系统。另外,根据上面流输出的概念,举一反三,可以弄出很多用法,例如NeoDynamic的条形码控件,你会发现条形码图片的路径居然就是本页页面,他巧妙地将对本页的请求通过对几个特征参数的判定而转向生成图片流,从而达到了不添加任何页面,不借助文件系统的“神奇效果”,只需要一个DLL即可使用。
    另外,很多人问生成验证码图片,也可以使用这样的思路,自己做一个这样的控件,或者网页。如果能做自定义控件更好。相信大家有这个能力。
    最后,还是提倡大家都去翻翻MSDN,有很大帮助。安装VS的时,7CD装的版本其中三张是MSDN Library,包含了近乎所有你想要了解的东西。如果有时间,不妨多访问一下MSDN中国,http://www.microsoft.com/china/msdn ,不定期会有一些优秀的文章在上面。

使用Refector来查看程序集,我已经将它转变为C#代码,如下:
其实稍微用一下心,熟练掌握了这几种语言的区别,很容易进行语言变换。参考第一篇文章,看看有什么不同和改进呢?

程序代码:

private void Page_Load(object sender, EventArgs e)
{
      this.Response.Clear();
      try
      {
            string originalFileName = this.Server.MapPath(this.Request["fn"]);
            int thumbnailWidth = IntegerType.FromString(this.Request["tw"]);
            int thumbnailHeight = IntegerType.FromString(this.Request["th"]);
            Image img = Image.FromFile(originalFileName);
            ImageFormat thisFormat = img.RawFormat;
            Size newSize = this.GetNewSize(thumbnailWidth, thumbnailHeight, img.Width, img.Height);
            Bitmap outBmp = new Bitmap(thumbnailWidth, thumbnailHeight);
            Graphics g = Graphics.FromImage(outBmp);
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.Clear(Color.FromArgb(0xff, 0xf9, 0xff, 240));
            Rectangle _Vb_t_record_0 = new Rectangle((int) Math.Round((double) (((double) (thumbnailWidth - newSize.Width)) / 2)), (int) Math.Round((double) (((double) (thumbnailHeight - newSize.Height)) / 2)), newSize.Width, newSize.Height);
            g.DrawImage(img, _Vb_t_record_0, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel);
            g.Dispose();
            if (thisFormat.Equals(ImageFormat.Gif))
            {
                  this.Response.ContentType = "image/gif";
            }
            else
            {
                  this.Response.ContentType = "image/jpeg";
            }
            EncoderParameters encoderParams = new EncoderParameters();
            long[] quality = new long[] { 100 };
            EncoderParameter encoderParam = new EncoderParameter(Encoder.Quality, quality);
            encoderParams.Param[0] = encoderParam;
            ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders();
            ImageCodecInfo jpegICI = null;
            int _Vb_t_i4_0 = arrayICI.Length - 1;
            for (int fwd = 0; fwd <= _Vb_t_i4_0; fwd++)
            {
                  if (arrayICI[fwd].FormatDescription.Equals("JPEG"))
                  {
                        jpegICI = arrayICI[fwd];
                        break;
                  }
            }
            if (jpegICI != null)
            {
                  outBmp.Save(this.Response.OutputStream, jpegICI, encoderParams);
            }
            else
            {
                  outBmp.Save(this.Response.OutputStream, thisFormat);
            }
      }
      catch (Exception exception1)
      {
            ProjectData.SetProjectError(exception1);
            Exception ex = exception1;
            ProjectData.ClearProjectError();
            return;
      }
}

标签:N/A

 

在线学习答案查询入口
微信扫码联系
微信扫码联系