摘要:大多數 Web 站點的用戶界面都需要有圖像,這些圖像通常存儲在磁盤上。本文介紹如何從程序集提供圖像。從程序集提供圖像可以避免眾多文件散布在磁盤上,簡化 Web 服務器的安裝和配置,以及提高圖像的安全性。(本文包含一些指向英文站點的鏈接)
適用于: Microsoft® .NET Framework 版本 1.0 和 1.1 Microsoft® Visual C#® Microsoft® ASP.NET
下載 MFRImages.exe 示例文件。下載內容中包括 readme.htm,用于說明如何配置示例。
目錄: 簡介 問題概述 提供圖像 從 ASPX 頁面提供圖像 從自定義處理程序提供圖像
簡介
經常會聽到那些編寫過 Windows 控件,后來又轉向 Web 的程序員抱怨:“為什么不能像控件那樣將圖像存儲在同一個程序集內呢?” 答案是可以,您只需要知道如何做到這一點。本文介紹如何從一個程序集內提供圖像,并提供了兩種檢索圖像的方法。下載本頁頂端的 MFRImages.exe,以便完整地了解下面討論的示例代碼。
問題概述
Web 站點上的圖像通常通過 URL 來引用,例如 。/article/UploadPic/2006-1/20061819323905.gif它告訴 Web 客戶端到哪里去查找圖像。在 Web 頁中,圖像與文字是分別下載的。圖像通常存儲在 Web 服務器中命名為 /images 等名稱的子目錄中,頁面只是提供到這些圖像的引用,以使它們能夠顯示在客戶端的瀏覽器中。
當創建自定義控件時,情況也是如此 - 圖像被用戶下載之前通常需要存儲在磁盤上。作為控件的編寫者,您可能希望能夠提供一個程序集,其中不僅包括控件,而且還包括相應的默認圖像。控件保持獨立完整很有益,因為它只需要用戶進行較少的配置。(負責配置的用戶可能會忘記將大量圖像文件復制到生產服務器上,從而導致其他用戶的不滿。)
希望將資源綁定到程序集的另外一個原因可能是為了確保用戶不能更改這些資源 - 例如對于公司商標,應始終使用特定的圖像,而不能由某個用戶將錯誤的圖像(如不正確的字樣或顏色)誤保存到磁盤中。如果已對您的程序集進行了嚴格命名,當加載該程序集時,可以進行測試以確保該程序集未被篡改。
從程序集提供圖像的主要問題是 HTTP 需要通過 URL 獲得圖像 - 您不能只將直接插入 HTML 的一串字節返回給用戶,就期望能夠正確顯示圖像。設法將圖像請求重定向到程序集內的某個資源是很有必要的,在本文中我將介紹兩種方法。
在繼續下面的內容之前,還要注意一個問題。如果在 Web 服務器上沒有進行其他一些配置,想提供一個能夠正確呈現圖像的完全獨立控件是不可能的。您還需要在服務器上創建其他的文件(至少一個),或對 IIS 配置數據庫做一些更改,以將圖像提供給客戶端。不過,完成這些簡單的更改后,您就可以輕松地從任何程序集提供圖像了。
提供圖像
既然問題的關鍵是要提供圖像,那么我們現在就進入正題。當從調用方傳來信息后,我們需要找到一種方法來加載圖像并在響應流中返回圖像。請注意,此示例中提供的圖像只有 .gif 一種類型 - 要提供其他類型的圖像,則需要通過圖像擴展名來推斷內容類型和圖像格式。
以下函數顯示如何從給定的程序集加載圖像,并通過 HttpResponse 流返回給客戶端。我們將把此函數作為后面代碼的基礎,并在接下來的內容中添加更有用的功能,例如異常處理和圖像緩存。我將把此函數定義為 ManifestImageLoader 類中的靜態函數。
public class ManifestImageLoader { public static void RenderImage ( string assembly , string image , HttpContext context ) { Assembly resourceAssem = Assembly.Load ( assembly ) ;
// 獲取資源 using ( Stream imageStream = resourceAssem.GetManifestResourceStream ( image ) ) { // 如果可以,將其寫出 using ( System.Drawing.Image theImage = System.Drawing.Image.FromStream ( imageStream ) ) response.ContentType = "image/gif" ; theImage.Save ( context.Response.OutputStream , ImageFormat.Gif ) ; } } }
函數 RenderImage 接受程序集名稱、圖像名稱和響應流。有了有效的程序集名稱和圖像名稱,加載圖像并將其返回到輸出流就輕而易舉了。首先加載程序集,然后使用 Assembly.GetManifestResourceStream 函數返回已命名資源(在此實例中為圖像)的數據流。您需要為 System.Reflection 和 System.IO 添加 using 子句,使其能夠通過編譯,并引用 System.Drawing 程序集。
有了圖像數據流之后,可以使用 Image.FromStream() 方法從該字節流構造圖像 - 請注意,我們使用的是 System.Drawing 中的圖像類,而不是 System.Web.UI.WebControls 中具有類似名稱的類,因為前者具有訪問 Win32 圖像函數的權限,而后者在 Web 控件中包含了 標記。
您或許不熟悉 C# using 語法,該語法在 Try/Finally 塊中包含了代碼,可以確保對括號中的項調用 Dispose。現在我們可以從程序集提供圖像了,首先需要能夠為該圖像創建 URL。第一種方法是使用 .ASPX 頁面。
從 ASPX 頁面提供圖像
第一種提供圖像的方法需要使用 .ASPX 頁面,該頁面通常要駐留在服務器上的某個位置。頁面本身不包含內容 - 它的主要功能是從 URL 檢索參數,并使用這些參數來檢索圖像。
例如,有一個名為 ImageFromASPX.aspx 的頁面位于 Web 站點的根目錄下。然后可以在 HTML 中使用以下 URL 編碼語法來定義從此頁面提供的所有圖像。這里我們提供的是名為 winxp.gif 的標題圖像。
<img src=http://cfan.net.cn/info/"/imagefromASPX.aspx?assem=ImageServer&image=winxp.gif" />
在 URL 中,我們定義了 ASPX 頁面的路徑,然后定義了兩個參數,一個是程序集名稱(在此實例中為 ImageServer),另一個是圖像名稱。這在安全性方面可能存在風險,因此在本文的后面我將介紹一種可用于此數據的加密方法。
在 ASPX 頁面的代碼中,可以寫入以下內容:
private void Page_Load ( object sender, System.EventArgs e ) { // 檢索參數 string assembly = Request.QueryString["assem"] ; string image = Request.QueryString["image"] ;
// 并加載圖像 ManifestImageLoader.RenderImage ( assembly , image ) ; }
代碼所做的就是根據請求分析參數,然后調用我們前面編寫的 RenderImage 函數。正如您看到的,這并不難實現。但它有一個缺點,即所有對圖像的請求都需要通過同一個 URL。也就是說,每個自定義控件都必須知道 imageserver.aspx 文件的位置和名稱,才能提供圖像。如何避免這個限制是下一節的主題。
從自定義處理程序提供圖像
如果您以前從未接觸過處理程序,我將在這里大概介紹一下。處理程序是用于實現 IHttpHandler 接口的對象。當包含給定文件擴展名的請求通過 ASP.NET 管道時,將為特定動詞(如 POST、GET 等)或一組動詞調用處理程序。
通常,ASP.NET 會檢查請求的文件擴展名,并將請求傳送給與該擴展名相關聯的處理程序。了解了這些知識,我們就可以創建處理程序,將其與自己的文件擴展名相關聯(這樣,ASP.NET 就會知道我們要調用的是處理程序,而不是其他內容),并且以該方式提供圖像。
下面的代碼顯示的是一個簡單的處理程序,該處理程序使用了上面聲明的 RenderImage 函數。
public class ManifestResourceHandler : IHttpHandler { /// <summary> /// 處理圖像請求 /// </summary> /// <param name="context">The current HTTP context</param> void IHttpHandler.ProcessRequest ( System.Web.HttpContext context ) { // 從請求中獲取程序集名稱和資源名稱 string assembly = context.Request.QueryString["assem"] ; string image = context.Request .QueryString["image"] ;
// 然后加載圖像并返回給調用方 ManifestImageLoader.RenderImage ( assembly , image ) ; }
/// <summary> /// 此處理程序可以重復使用,不需要循環 /// </summary> bool IHttpHandler.IsReusable { get { return true; } } }
這段代碼與上面為 ASPX 頁面顯示的代碼非常類似 - 從傳來的 URL 讀取參數,然后將這些參數傳遞到 RenderImage 函數。
現在,要想使用處理程序來提供圖像,我們需要使用不同的 URL。在此實例中,需要創建一個虛構的文件擴展名(即在 IIS 中不存在的擴展名),這樣,圖像請求就可以傳送給正確的處理程序。在此示例中我將使用擴展名“mfr”(表示“清單資源”)。圖像請求現在看起來有點像下面的描述。
<img src=http://cfan.net.cn/info/".mfr?assem=MS.Resources&image=winxp.gif" />
注意,我還未指定資源的路徑,只是指定了文件擴展名 .mfr。
使用處理程序的主要好處是可以為所有請求調用該程序,而不用考慮它們的路徑。要使處理程序能夠工作還需要另外兩個步驟。首先,需要修改 web.config,以指定新的處理程序:
<configuration> <system.web> ... <httpHandlers> <add verb="GET" path="*.mfr" type="ImageServer.ManifestResourceHandler, ImageServer" /> </httpHandlers> </system.web> </configuration>
上述配置文件中的類型定義了實現處理程序的類型和程序集。注意,動詞屬性區分大小寫,因此應設置為 GET 而不是其他的大小寫形式。程序集本身需要駐留在您 Web 站點的二進制目錄中,或安裝在全局程序集緩存 (GAC) 中。
其次,您需要在 IIS 管理中編輯 Web 服務器的配置。單擊您要更改的 Web 站點的 Properties(屬性),選擇 Home Directory(主目錄)選項卡,然后單擊 Configuration(配置)。將顯示與以下窗口類似的窗口。
單擊 Add(添加)按鈕,為 .mfr 文件類型創建條目。每個擴展名都會被映射到處理資源請求的 ISAPI 過濾器。對于 ASP.NET,為 aspnet_isapi.dll 過濾器。此庫駐留在磁盤中已安裝的 Framework 下,因此要設置 .mfr 擴展名的所有請求以通過相應的 ISAPI dll,需要進行如下設置:
版本 路徑 1.0 C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\aspnet_isapi.dll 1.1 C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet_isapi.dll
其余的設置顯示在以下圖像中。請注意,必須清除 Verify that file exists(檢查文件是否存在)復選框,否則永遠不會調用處理程序(因為磁盤中不存在實際的 .mfr 文件)。
現在應該可以運行處理程序了。在瀏覽器中鍵入映射到程序集中的資源的 URL:
如果您接收到的不是請求的圖像,而是一個異常(如“‘null’不是‘stream’的有效值”),那么您可能遇到了目前我們還沒有在代碼中進行處理的一些小問題 - 如果圖像有錯誤怎么辦? 我們將在下一節對這個問題及其他一
|