首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET 自訂控制項實作無縫圖片上傳:即時預覽、縮圖生成與自動清理

     編輯:ASP.NET 基礎

功能概覽與架構設計

本控制項解決傳統 <asp:FileUpload> 需整頁 PostBack 的體驗缺口,採用 隱藏 Iframe + 父頁面 JavaScript 回呼 架構,實現「無整頁重新整理」的上傳體驗,並整合縮圖生成、格式驗證、檔名衝突處理、原圖自動清理等企業級功能。

控制項運行效果圖

核心元件架構

檔案職責
CtFileUpLoad.ascx使用者控制項,含 Iframe、隱藏 TextBox 存檔名、預覽區 DIV
CtFileUpLoad.ascx.cs屬性定義、參數組裝傳遞給 Iframe
FileUpLoad.aspxIframe 內實際上傳頁面,含 FileUpload 控制項
FileUpLoad.aspx.cs伺服器端處理:儲存、縮圖、水印、回呼父頁面

關鍵程式碼解析

1. 使用者控制項標記

<%@ Control Language="C#" AutoEventWireup="true" 
    CodeFile="CtFileUpLoad.ascx.cs" Inherits="WebParts_CtFileUpLoad" %>

<table cellpadding="0" cellspacing="0">
<tr>
  <td>
    <iframe src="/WebParts/FileUpLoad.aspx?<%=ParsValue %>" 
        width="240px" height="22px" frameborder="0" scrolling="no"></iframe>
  </td>
  <td>
    <asp:TextBox runat="server" ID="tbFileName" style="display:none"></asp:TextBox>
    <div id="dvImg" runat="server" style="position:absolute; margin-left:50px;"></div>
  </td>
</tr>
</table>

<script>
function <%=ClientID %>CallLoaded() {
    <% =OnLoaded %>;
}
</script>

2. 控制項屬性設計

public partial class WebParts_CtFileUpLoad : UserControl
{
    public bool AutoFileName { get; set; } = true;           // 依時間戳自動命名
    public string UpLoadPath { get; set; } = "UPLOADFILES";  // 上傳資料夾
    public string OnLoaded { get; set; } = "";               // 上傳完成客戶端回呼
    public string SupportExtension { get; set; } = "gif|png|jpeg|jpg";
    public bool ShowImg { get; set; } = true;                // 是否顯示預覽
    
    public string FileName => tbFileName.Text;               // 取得儲存檔名

    protected string ParsValue = "";
    
    protected void Page_Load(object sender, EventArgs e)
    {
        ParsValue = $"AutoFileName={AutoFileName}&SupportExtension={SupportExtension}" +
                    $"&UpLoadPath={UpLoadPath}&ID={ClientID}";
    }
}

3. Iframe 內上傳頁面 - 前端驗證

<asp:FileUpload ID="FileUpload1" runat="server" onchange="upload(this);" />
<asp:Button ID="btUp" runat="server" style="display:none" OnClick="btUp_Click" />

<script>
function upload(ob) {
    var expStr = new RegExp(".*(" + "<%=SupportExtension%>" + ")$", "i");
    if (!expStr.test(ob.value)) {
        alert("上傳檔案類型錯誤。\n支援格式:" + "<%=SupportExtension%>");
        return;
    }
    document.getElementById("<%=btUp.ClientID %>").click(); // 觸發伺服器端上傳
}
</script>

4. 伺服器端核心處理邏輯

protected void btUp_Click(object sender, EventArgs e)
{
    if (!FileUpload1.HasFile) return;

    // 1. 檔案格式驗證
    string ext = Path.GetExtension(FileUpload1.FileName).ToLower();
    string[] allowed = SupportExtension.Split('|');
    if (!allowed.Contains(ext.TrimStart('.'))) { ShowError("格式不支援"); return; }

    // 2. 檔案大小限制 (2MB)
    if (FileUpload1.PostedFile.ContentLength > 2 * 1024 * 1024) { ShowError("超過 2MB"); return; }

    // 3. 決定檔名
    string fileName = AutoFileName 
        ? $"{DateTime.Now.Ticks}{ext}" 
        : FileUpload1.FileName;
    string thumbName = "s_" + fileName;
    string savePath = Server.MapPath($"~/{UpLoadPath}/");
    string fullPath = Path.Combine(savePath, fileName);
    string thumbPath = Path.Combine(savePath, thumbName);

    // 4. 避免重複
    if (File.Exists(fullPath)) { ShowError("檔案已存在"); return; }

    try 
    {
        // 5. 儲存原圖
        FileUpload1.SaveAs(fullPath);
        
        // 6. 產生縮圖 (根據原圖尺寸動態決定規格)
        using (var img = Image.FromFile(fullPath))
        {
            if (img.Width > 800 || img.Height > 600)
                MakeThumbnail(fullPath, thumbPath, 800, 600, "W");
            else
                MakeThumbnail(fullPath, thumbPath, 300, 230, "W");
        }

        // 7. 回呼父頁面:填入隱藏欄位、顯示縮圖、觸發 OnLoaded
        string script = $@"
            parent.document.getElementById('{ClientID}_tbFileName').value = '{thumbName}';
            parent.{ClientID}CallLoaded();
            var dv = parent.document.getElementById('{ClientID}_dvImg');
            dv.innerHTML = '<img src=\"{UpLoadPath}/{thumbName}\" style=\"width:{w}px;height:{h}px\" />';
            dv.style.display = 'block';
            alert('上傳成功');
        ";
        ClientScript.RegisterStartupScript(GetType(), "success", script, true);
    }
    catch (Exception ex) { ShowError(ex.Message); }
    finally
    {
        // 8. 關鍵:刪除原圖,只保留縮圖
        if (File.Exists(fullPath)) File.Delete(fullPath);
    }
}

5. 通用縮圖生成方法

public void MakeThumbnail(string src, string dest, int width, int height, string mode)
{
    using (var srcImg = Image.FromFile(src))
    {
        int toW = width, toH = height, x = 0, y = 0, ow = srcImg.Width, oh = srcImg.Height;
        
        switch (mode)
        {
            case "W": toH = oh * width / ow; break;           // 固定寬,高按比例
            case "H": toW = ow * height / oh; break;          // 固定高,寬按比例
            case "Cut":                                       // 裁切不變形
                if ((double)ow / oh > (double)toW / toH)
                { oh = srcImg.Height; ow = oh * toW / toH; y = 0; x = (srcImg.Width - ow) / 2; }
                else
                { ow = srcImg.Width; oh = ow * toH / toW; x = 0; y = (srcImg.Height - oh) / 2; }
                break;
        }

        using (var bmp = new Bitmap(toW, toH))
        using (var g = Graphics.FromImage(bmp))
        {
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.Clear(Color.Transparent);
            g.DrawImage(srcImg, new Rectangle(0, 0, toW, toH), 
                        new Rectangle(x, y, ow, oh), GraphicsUnit.Pixel);
            bmp.Save(dest, ImageFormat.Jpeg);
        }
    }
}

使用方式

<%@ Register Src="~/WebParts/CtFileUpLoad.ascx" TagPrefix="uc" TagName="FileUpload" %>

<uc:FileUpload ID="fuProductImage" runat="server" 
    UpLoadPath="ProductImages" 
    SupportExtension="jpg|png|gif" 
    AutoFileName="true" 
    OnLoaded="onImageUploaded" 
    ShowImg="true" />

<script>
function onImageUploaded() {
    var fileName = document.getElementById('<%=fuProductImage.ClientID%>_tbFileName').value;
    console.log('上傳完成:', fileName);
    // 可觸發後續邏輯:啟用送出按鈕、更新預覽區等
}
</script>

進階擴充建議

  • 多檔上傳:改用 HTML5 multiple 屬性 + FormData AJAX 上傳
  • 拖曳上傳:整合 Dropzone.js 或原生 Drag & Drop API
  • 雲端儲存:上傳至 Azure Blob / AWS S3,回傳 CDN URL
  • 圖片處理管線:整合 ImageSharp 替代 System.Drawing (跨平台、效能更佳)
  • 進度條:SignalR 或 XMLHttpRequest upload 事件回報進度

總結

此控制項展示了 ASP.NET WebForms 時代解決「無刷新上傳」的經典模式:Iframe 隔離上傳流程 + 父子頁面 JavaScript 通訊 + 伺服器端完整影像處理管線。雖然現代專案多改用 WebAPI + 前端框架,但此架構在維護舊系統、快速原型開發、內部管理後台等場景依然具備高開發效率與零前端建置依賴的優勢。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved