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

ASP.NET 多檔案上傳並自動產生縮圖完整實作:前後端程式碼與驗證邏輯

     編輯:ASP.NET 基礎

前端頁面


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="upload.aspx.cs" Inherits="upload" %>
<!DOCTYPE html>
<html xmlns="https://www.w3.org/1999/xhtml">
<head runat="server">
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>多檔上傳縮圖</title>
  <script src="js/jquery-1.6.2.min.js"></script>
  <script>
    function showError(inputId, msg) {
      $(inputId).parent().append("<span class='text-danger'>" + msg + "</span>");
    }
  </script>
  <style> li { list-style:none; padding-top:10px; } </style>
</head>
<body>
  <form id="form1" runat="server" enctype="multipart/form-data" method="post">
    <ul>
      <li><input type="file" id="upload1" name="upload" /></li>
      <li><input type="file" id="upload2" name="upload" /></li>
      <li><input type="file" id="upload3" name="upload" /></li>
      <li><input type="file" id="upload4" name="upload" /></li>
      <li><input type="file" id="upload5" name="upload" /></li>
      <li><input type="submit" id="btnPostFile" runat="server" onserverclick="btnPostFile_ServerClick" value="開始上傳" /></li>
    </ul>
  </form>
</body>
</html>

後端核心邏輯


protected void btnPostFile_ServerClick(object sender, EventArgs e)
{
    string saveRoot = Server.MapPath("~/uploadImg");
    const int maxBytes = 5 * 1024 * 1024; // 5MB

    if (!Directory.Exists(saveRoot)) Directory.CreateDirectory(saveRoot);

    if (Request.Files.Count > 0)
    {
        for (int i = 0; i < Request.Files.Count; i++)
        {
            var posted = Request.Files[i];
            string clientId = "#upload" + (i + 1);
            string errorMsg = null;

            if (string.IsNullOrEmpty(posted.FileName)) continue;

            if (posted.ContentLength > maxBytes)
            {
                errorMsg = "檔案超過 5MB 限制";
            }
            else
            {
                string savePath = Path.Combine(saveRoot, posted.FileName);
                if (File.Exists(savePath))
                {
                    errorMsg = "檔案已存在:" + posted.FileName;
                }
                else if (IsValidImage(savePath))
                {
                    posted.SaveAs(savePath);
                    CreateThumbnail(posted.InputStream, posted.FileName, saveRoot);
                }
                else
                {
                    errorMsg = "僅允許 JPG/PNG 格式";
                    if (File.Exists(savePath)) File.Delete(savePath);
                }
            }

            if (errorMsg != null)
                Page.ClientScript.RegisterStartupScript(GetType(), "", $"showError('{clientId}', '{errorMsg}');", true);
        }
    }
}

圖片格式驗證(檔頭魔數)


private bool IsValidImage(string filePath)
{
    using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
    using (var br = new BinaryReader(fs))
    {
        try
        {
            byte b1 = br.ReadByte();
            byte b2 = br.ReadByte();
            string magic = b1.ToString() + b2.ToString(); // 簡易串接
            // JPEG: FF D8 (255 216) | PNG: 89 50 (137 80)
            return magic == "255216" || magic == "13780";
        }
        catch { return false; }
    }
}

改進建議:使用 Span<byte> 讀取前 8 位元組,完整比對 PNG (89 50 4E 47 0D 0A 1A 0A) 與 JPEG (FF D8 FF) 標準魔數,避免誤判。

縮圖產生(高品質插值)


private void CreateThumbnail(Stream inputStream, string originalFileName, string saveRoot)
{
    using (var img = Image.FromStream(inputStream))
    {
        const int maxWidth = 100, maxHeight = 80;
        int nW, nH;
        if (img.Width > img.Height)
        {
            nW = maxWidth;
            nH = (int)Math.Floor((double)img.Height * maxWidth / img.Width);
        }
        else
        {
            nH = maxHeight;
            nW = (int)Math.Floor((double)img.Width * maxHeight / img.Height);
        }

        using (var bmp = new Bitmap(nW, nH))
        using (var g = Graphics.FromImage(bmp))
        {
            g.Clear(Color.Transparent);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.DrawImage(img, new Rectangle(0, 0, nW, nH), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);

            string thumbName = Path.GetFileNameWithoutExtension(originalFileName) + "_small" + Path.GetExtension(originalFileName);
            bmp.Save(Path.Combine(saveRoot, thumbName), img.RawFormat);
        }
    }
}

待改進項目(技術債)

  • 前端檔案輸入應改用 <input type="file" multiple> 單一控件支援多選。
  • 上傳驗證前移至客戶端(File API 檢查大小、類型),減少無效上傳頻寬。
  • 儲存路徑採用 GUID 重命名,避免檔名衝突與安全風險。
  • 縮圖產生改為背景佇列(Hangfire/HostedService),避免阻塞請求執行緒。
  • 引入 ImageSharp 取代 System.Drawing,支援 Linux 容器化、無 GDI+ 相依。
  • 完整錯誤處理、日誌記錄、單元測試覆蓋。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved