65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 HTTP 模块动态调整图像大小

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.89/5 (8投票s)

2008 年 8 月 24 日

CPOL

3分钟阅读

viewsIcon

66210

downloadIcon

751

通过在图像名称后附加高度和宽度,或者使用具有设定高度和宽度的自定义命名字段,轻松动态地调整整个网站的图像大小。

引言

为您的网站调整图像大小不必很困难。通过 HTTP 模块的魔力,我们可以拦截通过您网站的每一种特定类型的请求(例如图像请求),并在将结果发送到浏览器之前执行任何必要的处理。

当您需要调整整个网站的图像大小的时候,这非常方便。 下面的代码允许您

  • 通过在图像后添加宽度和高度来调整图像大小。
  • 定义具有特定宽度和高度的图像“键”,这样您就可以在配置文件中更改键的宽度和高度,并且网站上具有该键的每个图像都将自动调整大小。
  • 只需在web.config文件中添加几行代码,并且如果您想使用图像键,则添加一个 XML 配置文件,即可进行这些更改。
  • 配置设置和调整大小的图像列表被缓存,以使此解决方案尽可能具有可伸缩性。
  • 调整大小的图像会保存到磁盘,然后用于所有未来的请求,同样是为了可伸缩性。

查看一个动态图像调整大小的例子,了解图像调整大小可以多么容易。查看源代码,您会发现所有示例都在使用相同的图像名称,并且只有宽度和高度在变化。

背景

了解 HTTP 模块及其工作原理将有助于理解这里发生的事情。您可以通过这篇来自 15 秒的文章简要了解该主题。

使用代码

要将此代码添加到现有网站,请将以下三行代码添加到您的web.config文件的httpHandlers部分。 这告诉 .NET 处理任何扩展名为.jpg.gif.png,后跟.ashx的内容,由ResizeImages类处理。

<add verb="*" path="*.jpg.ashx" type="ImageHandlers.ResizeImages"/>
<add verb="*" path="*.gif.ashx" type="ImageHandlers.ResizeImages"/>
<add verb="*" path="*.png.ashx" type="ImageHandlers.ResizeImages"/>

接下来,在您的appSettings部分中,定义您想要保存调整大小的图像的位置,以及您将用于图像键的 XML 文件的位置(如果您不打算使用图像键,则第二个设置是可选的)。

<add key="ResizedImagesDirectory" value="~/images/resized"/>
<add key="ResizedImageKeys" value="~/images/resized.xml"/>

ResizeImages.vb类放到您的App_Code文件夹中,并将您的图像链接添加到测试页面。

您的链接可以以几种不同的格式显示

<img src="images/turtle.jpg.ashx?k=normal" />     <!--With an image key-->
<img src="images/turtle.jpg.ashx?h=100" />    <!--Height only (width is scaled)-->
<img src="images/turtle.jpg.ashx?w=100" />    <!--Width only (height is scaled)-->
<img src="images/turtle.jpg.ashx?h=100&w=100" />  <!--Height and width set-->

如果您想定义图像键的高度和宽度,则该信息将保存在resized.xml文件中(或者您选择的任何名称的文件)。格式如下

<ResizedImages>
  <image name="thumbnail" width="100" height="100" />
  <image name="normal" width="200" />  
  <image name="large" height="300" />  
</ResizedImages>

因此,在此示例中,具有图像键“normal”的图像的宽度将设置为 200。 如果更改图像键的宽度,则您网站上所有使用“normal”键的图像都将自动调整大小。

代码

ResizeImages.vb类处理所有图像大小调整。如果您对此如何工作感兴趣,我已经注释了下面的代码,您可以更详细地研究它。如果您只是想开始使用所有内容,请下载该项目并享受吧!

注意:这目前仅适用于 .NET 3.5,因为我使用 LINQ 来解析 XML 中的图像键。 如果您想在 .NET 2.0 中运行它,您可以替换GetImageDimensions函数中的 LINQ 代码,并改为使用 XPath 查询。

Imports System
Imports System.Text
Imports System.Web
Imports System.Drawing
Imports System.Web.SessionState
Imports System.IO
Imports System.Xml
Imports System.Collections.Generic
Imports System.Linq
Imports System.Xml.Linq

Namespace ImageHandlers
    Public Class ResizeImages
        Implements IHttpHandler

        Enum ImageType As Integer
            JPG
            PNG
            GIF
        End Enum

        Public ReadOnly Property IsReusable() As Boolean _
               Implements System.Web.IHttpHandler.IsReusable
            Get
                Return True
            End Get
        End Property

        Dim ResizedImagesDirectory As String = _
          ConfigurationManager.AppSettings("ResizedImagesDirectory")

        Public Sub ProcessRequest(ByVal Ctx As System.Web.HttpContext) _
               Implements System.Web.IHttpHandler.ProcessRequest

            'Get the current request
            Dim Req As HttpRequest = Ctx.Request


            'Set the width and height for the resized image
            Dim Width As Integer = 0
            Dim Height As Integer = 0
            Dim Key As String = ""
            If Not IsNothing(Req.QueryString("w")) And _
                   IsNumeric(Req.QueryString("w")) Then
                Width = Req.QueryString("w")
            End If
            If Not IsNothing(Req.QueryString("h")) And _
                   IsNumeric(Req.QueryString("h")) Then
                Height = Req.QueryString("h")
            End If
            If Not IsNothing(Req.QueryString("k")) Then
                Key = Req.QueryString("k")
            End If

            'If we have a key stored in an xml file, use it to determine 
            'the width and height of the image instead
            If Key.Length > 0 Then
                Dim KeyImage As New ResizedImage
                KeyImage = GetImageDimensions(Ctx, Key)
                Height = KeyImage.Height
                Width = KeyImage.Width
            End If

            Dim DisplayResizedImage As Boolean = True
            If Width = 0 And Height = 0 Then
                'They didn't set a height or width, 
                'so don't create or display a resized image
                'Use the original image instead
                DisplayResizedImage = False
            End If

            'Get the path of the file, without the .ashx extension
            Dim PhysicalPath As String = Regex.Replace(Req.PhysicalPath, _
                                           "\.ashx.*", "")

            'Determine the content type, and save 
            'what image type we have for later use
            Dim ImgType As ImageType
            If PhysicalPath.EndsWith(".jpg") Or _
                   PhysicalPath.EndsWith(".jpeg") Then
                Ctx.Response.ContentType = "image/jpeg"
                ImgType = ImageType.JPG
            ElseIf PhysicalPath.EndsWith(".gif") Then
                Ctx.Response.ContentType = "image/gif"
                ImgType = ImageType.GIF
            ElseIf PhysicalPath.EndsWith(".png") Then
                Ctx.Response.ContentType = "image/png"
                ImgType = ImageType.PNG
            End If


            'Name the images based on their width, height, 
            'and path to ensure they're unique.
            'The image name starts out looking like 
            '/HttpModule/images/turtle.jpg (the virtual path), and gets
            'converted to 400_200_images_turtle.jpg.
            'The 400 is the width, and the 200 is the height.
            'If a width or height is not specified,
            'it will look like 0_200_images_turtle.jpg (an example
            'where the width is not specified).
            Dim VirtualPath As String = Regex.Replace(Req.Path, "\.ashx.*", "")
            Dim ResizedImageName As String = Regex.Replace(VirtualPath, "/", "_")
            ResizedImageName = Regex.Replace(ResizedImageName, "_.*?_", "")
            ResizedImageName = Width & "_" & Height & _
                               "_" & ResizedImageName

            'Get the resized image
            Dim ri As New ResizedImage
            ri = GetResizedImage(Ctx, ResizedImageName, Height, Width, ImgType)

            Try
                If DisplayResizedImage Then 'display resized image
                    Ctx.Response.WriteFile(Path.Combine(ri.ImagePath, ri.ImageName))
                Else
                    'display original image
                    Ctx.Response.WriteFile(PhysicalPath)
                End If

            Catch ex As Exception
                'You can add logging here if you want,
                'but most like the image path can't be found,
                'so don't do anything
            End Try

        End Sub

        Private Function GetResizedImage(ByVal Ctx As System.Web.HttpContext, _
                ByVal ImageName As String, ByVal Height As Integer, _
                ByVal Width As Integer, ByVal ImgType As ImageType) As ResizedImage

            'Look in the cache first for a list of images that have been resized
            Dim ResizedImageList As New List(Of ResizedImage)
            ResizedImageList = Ctx.Cache.Get("ResizedImageList")

            Dim ResizedImage As New ResizedImage
            Dim ImageFound As Boolean = False

            If IsNothing(ResizedImageList) Then
                'Nothing in the cache, start a new image list
                ResizedImageList = New List(Of ResizedImage)
            Else
                'Let's see if an image with this name and size is already created
                For Each ri As ResizedImage In ResizedImageList
                    If ri.ImageName = ImageName And ri.Height = Height _
                        And ri.Width = Width Then
                        'The image already exists, no need to create it.
                        ResizedImage = ri
                        ImageFound = True
                        Exit For
                    End If
                Next
            End If

            'Create the folder where we want to save 
            'the resized images if it's not already there
            Dim ResizedImagePath As String = _
                Ctx.Server.MapPath(ResizedImagesDirectory)
            If Not Directory.Exists(ResizedImagePath) Then
                Directory.CreateDirectory(ResizedImagePath)
            End If

            'Clear the cache anytime the resized image folder 
            'changes (in case items were removed from it)
            Dim cd As New CacheDependency(ResizedImagePath)

            If Not ImageFound Then
                'We didn't find the image in the list of resized 
                'images...look in the resized folder
                'and see if it's there
                Dim ImageFullPath As String = _
                    Path.Combine(Ctx.Server.MapPath(ResizedImagesDirectory), ImageName)
                If File.Exists(ImageFullPath) Then
                    'The image has already been created, 
                    'set the properties for the image
                    'and add it to the cached image list
                    ResizedImage.ImageName = ImageName
                    ResizedImage.ImagePath = _
                           Ctx.Server.MapPath(ResizedImagesDirectory)
                    ResizedImage.Height = Height
                    ResizedImage.Width = Width
                    ResizedImageList.Add(ResizedImage)

                    'Keep the cache for a day, unless new 
                    'images get added to or deleted from
                    'the resized image folder
                    Dim ts As New TimeSpan(24, 0, 0)
                    Ctx.Cache.Add("ResizedImageList", ResizedImageList, cd, _
                                  Cache.NoAbsoluteExpiration, ts, _
                                  CacheItemPriority.Default, Nothing)
                End If
            End If

            Dim Req As HttpRequest = Ctx.Request
            Dim PhysicalPath As String = Regex.Replace(Req.PhysicalPath, _
                                         "\.ashx.*", "")
            If ResizedImage.ImageName = "" Then
                'The image isn't already created, 
                'we need to create it add it to the cache
                ResizeImage(PhysicalPath, ResizedImagePath, _
                            ImageName, Width, Height, ImgType)

                'Now update the cache since we've added a new resized image 
                ResizedImage.Width = Width
                ResizedImage.Height = Height
                ResizedImage.ImageName = ImageName
                ResizedImage.ImagePath = ResizedImagePath
                ResizedImageList.Add(ResizedImage)

                Dim ts As New TimeSpan(24, 0, 0)
                Ctx.Cache.Add("ResizedImageList", ResizedImageList, cd, _
                              Cache.NoAbsoluteExpiration, ts, _
                              CacheItemPriority.Default, Nothing)
            End If

            Return ResizedImage
        End Function

        Private Sub ResizeImage(ByVal ImagePath As String, _
                    ByVal ResizedSavePath As String, ByVal ResizedImageName As String, _
                    ByVal NewWidth As Integer, ByVal NewHeight As Integer, _
                    ByVal ImgType As ImageType)

            'Make sure the image exists before trying to resize it
            If File.Exists(ImagePath) And Not (NewHeight = 0 And NewWidth = 0) Then
                Using OriginalImage As New Bitmap(ImagePath)

                    If NewWidth > 0 And NewHeight = 0 Then
                        'The user only set the width, calculate the new height
                        NewHeight = Math.Floor(OriginalImage.Height / _
                                    (OriginalImage.Width / NewWidth))
                    End If

                    If NewHeight > 0 And NewWidth = 0 Then
                        'The user only set the height, calculate the width
                        NewWidth = Math.Floor(OriginalImage.Width / _
                                             (OriginalImage.Height / NewHeight))
                    End If

                    If NewHeight > OriginalImage.Height Or _
                                      NewWidth > OriginalImage.Width Then
                        'Keep the original height and width 
                        'to avoid losing image quality
                        NewHeight = OriginalImage.Height
                        NewWidth = OriginalImage.Width
                    End If


                    Using ResizedImage As New Bitmap(OriginalImage, NewWidth, NewHeight)
                        ResizedImage.SetResolution(72, 72)

                        Dim newGraphic As Graphics = Graphics.FromImage(ResizedImage)
                        newGraphic.Clear(Color.White)
                        newGraphic.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
                        newGraphic.InterpolationMode = _
                           Drawing2D.InterpolationMode.HighQualityBicubic
                        newGraphic.DrawImage(OriginalImage, 0, 0, NewWidth, NewHeight)

                        'Save the image as the appropriate type
                        Select Case ImgType
                            Case ImageType.GIF
                                ResizedImage.Save(System.IO.Path.Combine(ResizedSavePath, _
                                                  ResizedImageName), Imaging.ImageFormat.Gif)
                            Case ImageType.JPG
                                ResizedImage.Save(System.IO.Path.Combine(ResizedSavePath, _
                                                  ResizedImageName), Imaging.ImageFormat.Jpeg)
                            Case ImageType.PNG
                                ResizedImage.Save(System.IO.Path.Combine(ResizedSavePath, _
                                                  ResizedImageName), Imaging.ImageFormat.Png)
                        End Select

                    End Using
                End Using
            End If
        End Sub

        Private Function GetImageDimensions(ByVal Ctx As HttpContext, _
                         ByVal Key As String) As ResizedImage


            Dim ri As New ResizedImage

            'If I enter the key "thumbnail", this function 
            'will go to the xml file to find out
            'what the width and height of a thumbnail should be.

            'The xml that we're reading from looks like this.
            'You can set the width, the height, or both
            '<ResizedImages>
            '  <image name="thumbnail" width="100" height="100" />
            '  <image name="normal" width="200" />  
            '  <image name="large" height="300" />  
            '</ResizedImages>
            
            'Load the xml file
            Dim XMLSource As XElement = GetResizedImageKeys(Ctx)

            'Get all nodes where the name equals the key
            'To make this code work in .Net 2.0, use an xpath query to get the height
            'and width values instead of a LINQ query
            Dim ResizedQuery = From r In XMLSource.Elements("image") _
                            Where r.Attribute("name") = Key _
                            Select r

            'Set the resized image we're returning with the width and height
            For Each r As XElement In ResizedQuery
                If Not IsNothing(r.Attribute("height")) Then
                    ri.Height = r.Attribute("height")
                End If
                If Not IsNothing(r.Attribute("width")) Then
                    ri.Width = r.Attribute("width")
                End If
            Next

            Return ri
        End Function

        Private Function GetResizedImageKeys(ByVal ctx As HttpContext) As XElement
            Dim xel As XElement = Nothing
            Dim ResizedImageKeys As String = _
                ctx.Server.MapPath(ConfigurationManager.AppSettings("ResizedImageKeys"))
            If Not IsNothing(ResizedImageKeys) Then
                'Try to get the xml from the cache first
                xel = ctx.Cache.Get("ResizedImageKeys")

                'If it's not there, load the xml document and then add it to the cache
                If IsNothing(xel) Then
                    xel = XElement.Load(ResizedImageKeys)
                    Dim cd As New CacheDependency(ResizedImageKeys)
                    Dim ts As New TimeSpan(24, 0, 0)
                    ctx.Cache.Add("ResizedImageKeys", xel, cd, _
                                  Cache.NoAbsoluteExpiration, ts, _
                                  CacheItemPriority.Default, Nothing)
                End If
            End If
            Return xel
        End Function

        'This class is used to keep track of which images are resized.
        'We save this in a cached list and look here first,
        'so we don't have to look through the folder on the file
        'system every time we want to see if the resized image
        'exists or not
        Private Class ResizedImage
            Private _ImageName As String
            Private _ImagePath As String
            Private _Width As Integer
            Private _Height As Integer
            Public Property ImageName() As String
                Get
                    Return _ImageName
                End Get
                Set(ByVal value As String)
                    _ImageName = value
                End Set
            End Property
            Public Property ImagePath() As String
                Get
                    Return _ImagePath
                End Get
                Set(ByVal value As String)
                    _ImagePath = value
                End Set
            End Property
            Public Property Width() As Integer
                Get
                    Return _Width
                End Get
                Set(ByVal value As Integer)
                    _Width = value
                End Set
            End Property
            Public Property Height() As Integer
                Get
                    Return _Height
                End Get
                Set(ByVal value As Integer)
                    _Height = value
                End Set
            End Property
            Public Sub New()
                Width = 0
                Height = 0
                ImagePath = ""
                ImageName = ""
            End Sub
        End Class
    End Class

End Namespace
© . All rights reserved.