CODE-使用ASP.NET 4.0 Routing處理圖檔下載

網友Slash提問:

如何在ASP.NET實現將/boo/a.jpg格式的網址導向ashx進行檔案下載?

一時程式毒癮發作,索性把這個題目當成自己的ASP.NET 4 Routing私房練習題止癢,也看看是否能順便解答Slash的疑問。

ASP.NET從4.0起加入內建Routing功能,相較於3.5 SP1做了不少強化,例如: 增加PageRouteHandler以簡便地完成WebPage導向、提供HttpRequest.RequestContext.RouteData方便路徑參數存取... 等等[參考]。

在這個練習中,我打算分別用WebForm(aspx)及Generic Handler(ashx)各實做一次。首要之務是在Global.asax中註冊路由,其中有個小把戲: 在ASP.NET 4中,可以直接用PageRouteHandler處理WebForm導向,卻沒有為ashx提供相似的機制(依Phil Haack的說法,想到時已來不及加進去了),因此要用Phil提供的因應解法,自行宣告一個HttpHandlerRouteHandler類別解決。我們將images/{imgname}註冊給Download.ashx、imgs/{imgname}註冊給DownloadImage.aspx。

<%@ Application Language="C#" %>
<%@ Import Namespace = "System.Web.Routing" %>
<script runat="server">
    void Application_Start(object sender, EventArgs e) 
    {
        RegisterRoutes(RouteTable.Routes);
    }
    
    public static void RegisterRoutes(RouteCollection routes)
    {
        //分別為ashx及aspx註冊路由, 一個用images/*.*, 一個用imgs/*.*
        //ashx註冊時有個小Trick,見HttpHandlerRouteHandler說明
        routes.Add("Images", new Route("Images/{imgname}", 
            new HttpHandlerRouteHandler<DownloadImage>()));
        routes.MapPageRoute("Imgs", "Imgs/{imgname}",
                "~/DownloadImage.aspx");
    }
 
    //ASP.NET 4不支援直接將IHttpHandler視為RouterHandler(太晚想到)
    //在此引用Phil Haack的解決方案
    //http://haacked.com/archive/2009/11/04/routehandler-for-http-handlers.aspx
    public class HttpHandlerRouteHandler<THandler>
    : IRouteHandler where THandler : IHttpHandler, new()
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new THandler();
        }
    }

接著看DownloadImage.ashx。由context.Request.RequestContext.RouteData.Values[“imgname”]取出路徑中的圖檔名,檢查該檔是否存在App_Data下,有則以Response.WriteFile()傳回內容,否則丟出404的HttpException,讓IIS視同網頁不存在處理。

using System;
using System.Web;
using System.IO;
 
public class DownloadImage : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var routeData = context.Request.RequestContext.RouteData.Values;
        string file = context.Server.MapPath(
                      "~/App_Data/" + routeData["imgname"].ToString());
        if (File.Exists(file))
        {
            var resp = context.Response;
            //註: 理論上要由副檔名決定ContentType,此處省略
            resp.ContentType = "image/gif";
            resp.WriteFile(file);
            resp.End();
        }
        else
        {
            throw new HttpException(404, "HTTP/1.1 404 Not Found");
        }
    }
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

再來是DownloadImage.aspx,寫法跟ashx接近,唯一的差別是在WebForm中throw HttpException會被IIS當成程式出錯,觸發HTTP 500程式錯誤例外,無法模擬HTTP 404的狀況,即便狀態碼為404,但在未開啟CustomErrors時,會視同程式錯誤顯示例外所在的原始碼內容,開啟後則出現Runtime Error及web.config customErrors mode設定提示,與熟知的找不到網頁訊息形式有些出入,故改為設定StatusCode跟輸出Not Found訊息代替。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
 
public partial class DownloadImage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string file = Server.MapPath("~/App_Data/" + 
                        (RouteData.Values["imgname"] ?? string.Empty).ToString());
        if (File.Exists(file))
        {
            //註: 理論上要由副檔名決定ContentType,此處省略
            Response.ContentType = "image/gif";
            Response.WriteFile(file);
            Response.End();
        }
        else
        {
            //在WebForm中無法透過throw HttpException方式導向IIS 404錯誤頁
            //故在此自行模擬傳回HTTP 404結果
            Response.Clear();
            Response.StatusCode = 404;
            Response.Status = "404 Not Found";
            Response.Write("Not Found");
            Response.End();
        }
    }
}

執行結果如下:

  1. ashx正確讀取圖檔的情境
  2. ashx找不到圖檔的情境
  3. aspx正確讀取圖檔的情境
  4. aspx找不到圖檔的情境

歡迎推文分享:
Published 13 July 2012 06:24 AM 由 Jeffrey
Filed under:
Views: 16,980



意見

# 小黑 said on 15 July, 2012 01:43 AM

請問黑大,這手法是否同樣是適用 asp.net mvc 中?

# Ammon said on 15 July, 2012 06:02 AM

黑大,目前我還沒有遇過 aspx 拋出 HttpException 404 但 IIS 當成 500 的情況。即便是黃色死亡畫面,Status code 依然是 404。這中間是不是有甚麼誤會?

# Jeffrey said on 15 July, 2012 08:53 PM

to Ammon, 謝謝指正,的確狀態碼是404沒錯,原本的寫法有誤,我做了調整希望能更精準些。

...即便狀態碼為404,但在未開啟CustomErrors時,會視同程式錯誤顯示例外所在的原始碼內容,開啟後則出現Runtime Error及web.config customErrors mode設定提示,與熟知的找不到網頁訊息形式有些出入,故改為設定StatusCode跟輸出Not Found訊息代替。

# Jeffrey said on 15 July, 2012 08:55 PM

to 小黑, 在ASP.NET MVC中,可以用HttpNotFoundResult。

weblogs.asp.net/.../asp-net-mvc-3-using-httpnotfoundresult-action-result.aspx

# Ammon said on 16 July, 2012 12:28 AM

黑大,custom error 可以依據Status code 設定不同頁面

# Edward said on 08 January, 2013 06:18 AM

黑大您好:

我有一個圖片下載的問題,

我到美國gap集團的網頁看衣服,

發現一個奇怪的問題,

某些衣服點進去看後,

檢視原始碼時,

可以看到衣服圖片的網址,

如下:oldnavy.gap.com/.../product.do

而有些衣服點進去看後,

檢視原始碼時,

會出現img src='/',

找不到圖片網址,

但網頁還是看得到圖片,

這是什麼原因呢??

我利用HttpWebRequest、HttpWebResponse去下載該網站時,原始碼又和在瀏覽器時也不同,

這又是什麼原因呢??

不好意思,問題有點長,麻煩您撥空看一下,謝謝。

# Edward said on 08 January, 2013 06:36 AM

補上看得到圖片,但是src="/"的網址。

如下:

oldnavy.gap.com/.../product.do

請用{<div id="productContentLeft"}尋找,謝謝。

# Jeffrey said on 09 January, 2013 04:25 AM

to Edward, 你所說的src="/"應該是指這個:

<div id="productContentLeft" class="brand3">

 <noscript>

 <div><img alt='Main product image: Men&#39;s Striped Slub-Knit Tees' id="product_image"          src='/' /></div>

 </noscript>

<img>被包在<noscript>中,意思是當瀏覽器不支援JavaScript時才適用,絕大多數瀏覽器因為支援JavaScript,看到的圖片並不是這個<img>,而是 id="product_image_bg">裡的<img id="product_image">,而該<img>的src已被JavaScript動態切換成衣服的圖檔(選顏色後會換圖)。

View Source看到的是一開始下載回來的HTML內容,事後JavaScript會去更動其中的src,要使用IE Dev Tools或Firefox之類的工具去看即時的HTML,才是當下的狀態。

# Edward said on 09 January, 2013 08:45 PM

了解了,謝謝大大的指導。

# Edward said on 09 January, 2013 09:23 PM

黑大您好,可以另外再向您請教一件事嗎??

我在網址上打“oldnavy.gap.com/.../productData.do

看到的內容,跟用HttpWebRequest、HttpWebResponse去下載的內容會完全不一樣。

如果我曾經在瀏覽器上看過該網頁,就會一樣了,請教您知道原因是什麼嗎??謝謝

# Jeffrey said on 11 January, 2013 02:38 AM

to Edward, 沒帶Cookie,會被導向

/browse/productData.do?targetURL=http%3A%2F%2Foldnavy.gap.com%2Fbrowse%2FproductData.do%3Fpid%3D330654022&CookieSet=Set,設完Cookie後才連至真正的網址。我猜HttpWebRequest下載到的內容是一個HTTP/1.1 302 Moved Temporarily轉址回應,你不妨比對看看。

# Edward said on 20 January, 2013 11:03 PM

嗯,謝謝黑大,

後來觀察封包後,

發現是cookie在作怪,

已經解決該問題了,

感激不盡

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<July 2012>
SunMonTueWedThuFriSat
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication