HTML5 Canvas塗鴉板裡,雖然提供了匯出圖檔功能,卻必須由使用者自行在網頁的圖檔上按右鍵另存檔案,只能算半自助的服務。理想做法應比照一般網頁下載操作,在按鈕後直接跳出存檔提示,讓使用者決定存檔位置及檔名等,較符合一般使用者期望。

我在網路上找到Jacob Seidelin實作的cavas2imgage.js程式庫,追了一下程式碼發現是透過location.href = DataURI字串實現由Javascript觸發瀏覽器直接下載檔案,但我發現它有一些缺點: 一來無法指定檔案名,二來並不適用全部的瀏覽器。看來還是得仰賴伺服器端的"內應",才能達到較好的效果。

這點小事自然難不倒ASP.NET,以下便是伺服器端內應的範例(DownloadImage.ashx)。運作流程包含兩個階段,要下載圖檔時,先透過jQuery.post()將DataURI字串送至ASP.NET端,再由C#程式將其還原成byte[]存入Cache,並動態產生一個GUID為Cache Key傳回Javascript端;前端在取得GUID Key後,即可以location.href = "DownloadImageFile.ashx?k=" + GUIDKey的方式再度呼叫ASP.NET程式,此時再透過傳入的GUID由Cache取出byte[],以檔案下載MIME Type(application/octect-stream)並指定以下載時間決定的檔案名傳回PNG圖檔。由於此作業僅為一次性,在下載完成後即可將Cache內的資料清除,節省空間。

<%@ WebHandler Language="C#" Class="DownloadImage" %>
using System;
using System.Web;
using System.Web.Caching;
 
public class DownloadImage : IHttpHandler {
    
    public void ProcessRequest (HttpContext context) {
        HttpRequest req = context.Request;
        HttpResponse resp = context.Response;
        try
        {
            if (req.HttpMethod == "POST")
            {
                string header = "data:image/png;base64,";
                string data = req.Form["data"];
                //檢查上傳內容為PNG Data URI才處理
                if (data.StartsWith(header))
                {
                    //隨機產生GUID
                    string k = Guid.NewGuid().ToString();
                    //將Data URI轉成byte[]以GUID為Key存內Cache
                    HttpContext.Current.Cache.Add(k,
                        Convert.FromBase64String(data.Substring(header.Length)),
                        null, DateTime.Now.AddSeconds(60),
                        Cache.NoSlidingExpiration, CacheItemPriority.High, null);
                    //回傳GUID
                    resp.Write(k);
                }
            }
            else if (req.HttpMethod == "GET")
            {
                string k = req.QueryString["k"];
                //由前方傳入Key參數提取Cache中的資料
                if (HttpContext.Current.Cache[k] != null)
                {
                    //以檔案下載方式回傳byte[]內容
                    resp.AddHeader("content-disposition",
        string.Format("attachment;filename={0:yyyyMMddHHmmss}.png", DateTime.Now));
                    resp.ContentType = "application/octet-stream";
                    resp.BinaryWrite((byte[])HttpContext.Current.Cache[k]);
                    //清除Cache中的內容
                    HttpContext.Current.Cache.Remove(k);
                }
            }
        }
        catch (Exception ex)
        {
            resp.ContentType = "text/plain";
            resp.Write(ex.Message);
        }
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }
}

下載圖檔功能所需的Script也很簡單,不用五行就能打發:

    $("#bDownload").click(function () {
        $.post("DownloadImage.ashx", { data: $canvas[0].toDataURL() },
        function (k) {
            if (k)
                location.href = "DownloadImage.ashx?k=" + k;
        });
    });

備有線上展示,有興趣的人可以動手玩玩。


Comments

# by 黃蜂

請問: 在網路上找到Jacob Seidelin實作的cavas2imgage.js程式庫。 指的是這個嗎? https://github.com/hongru/canvas2image 如果是,那就是改網址了。

# by Jeffrey

to 黃蜂,你找到的應是其他人的版本,Jacob Seidelin的github在https://github.com/jseidelin 。nihilogic.dk網站目前是壞的,我找到一個備份 http://www.jsdelivr.com/#!canvas2image

# by 黃蜂

感謝 Jeffrey 回覆,您的部落格幫助我很多。

Post a comment