| 層級 | 檔案 | 職責 |
|---|---|---|
| 表現層 | Upfile.aspx | 上傳介面:FileUpload + Button + Label 回饋 |
| 代碼後置 | Upfile.aspx.cs | 協調上傳流程、實例化上傳類、處理結果 |
| 核心邏輯 | AllSheng.upload (Class Library) | 封裝所有上傳邏輯:驗證、命名、儲存、縮圖、資料庫記錄 |
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Upfile.aspx.cs" Inherits="Inc_Upfile" %>
<form id="form1" runat="server">
<asp:FileUpload ID="FileUpload1" runat="server" />
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="上傳檔案" CssClass="btn2" />
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</form>
public partial class Inc_Upfile : Page
{
protected void Button1_Click(object sender, EventArgs e)
{
var uploader = new AllSheng.upload();
uploader.Path = "../UpLoadfiles"; // 設定儲存路徑
uploader.Sizes = 200; // 限制 200KB
uploader.FileType = "jpg|gif|bmp|png|doc"; // 允許格式
string result = uploader.SaveAs(Request.Files).ToString();
Label1.Text = result; // 顯示儲存路徑
uploader = null;
}
}
namespace AllSheng
{
public class upload
{
#region 內部欄位
private HttpPostedFile postedFile;
private string localFileName, localFileExtension, localFilePath;
private string saveFileName, saveFileExtension, saveFilePath, saveFileFolderPath;
private string path = "uploadimages"; // 預設路徑
private string fileType = "jpg|gif|bmp|jpeg|png|rar|doc";
private int sizes = 200; // KB
#endregion
#region 公開屬性設定
public string Path { set => path = value; }
public int Sizes { set => sizes = value; }
public string FileType { set => fileType = value; }
#endregion
#region 核心上傳方法
public string SaveAs(HttpFileCollection files)
{
string myReturn = "";
try
{
for (int i = 0; i < files.Count; i++)
{
postedFile = files[i];
localFilePath = postedFile.FileName;
if (string.IsNullOrEmpty(localFilePath)) continue;
// 1. 檔案大小驗證
localFileLength = postedFile.ContentLength;
if (localFileLength >= sizes * 1024)
{ Message($"上傳檔案不可大於 {sizes}KB"); break; }
// 2. 取得儲存資料夾 (支援日期格式化路徑)
saveFileFolderPath = GetSaveFileFolderPath(path);
// 3. 產生唯一檔名:UTC 時間戳 + 原副檔名
localFileName = Path.GetFileName(postedFile.FileName);
saveFileName = DateTime.UtcNow.ToString("yyyyMMddHHmmssffffff");
// 4. 副檔名驗證
localFileExtension = GetFileExtension(localFileName);
if (string.IsNullOrEmpty(localFileExtension))
{ Message($"僅支援格式:{fileType}"); continue; }
saveFileExtension = localFileExtension;
saveFilePath = Path.Combine(saveFileFolderPath, saveFileName + saveFileExtension);
// 5. 實際儲存
postedFile.SaveAs(saveFilePath);
// 6. 回傳相對路徑供前端使用
myReturn += (myReturn == "" ? "" : "|") +
path.TrimStart('/') + saveFileName + saveFileExtension;
// 7. 整合 FCKeditor/CKEditor:直接寫入編輯器內容
string imgTag = $"<img src='{saveFileName}{saveFileExtension}' border=0 />";
string script = $"parent.Article_Content___Frame.FCK.EditorDocument.body.innerHTML+='{imgTag}'";
HttpContext.Current.Response.Write($"<script>{script}</script>");
}
}
catch { Message("發生未知錯誤!"); myReturn = null; }
return myReturn;
}
#endregion
#region 輔助方法
private string GetSaveFileFolderPath(string format)
{
string folderPath = format.Contains("yyyy") || format.Contains("MM") || format.Contains("dd")
? DateTime.UtcNow.ToString(format)
: format;
string fullPath = HttpContext.Current.Server.MapPath(".") + folderPath;
if (!Directory.Exists(fullPath)) Directory.CreateDirectory(fullPath);
return fullPath + "\\";
}
private string GetFileExtension(string fileName)
{
string ext = Path.GetExtension(fileName)?.ToLower();
if (string.IsNullOrEmpty(ext)) return null; // 無副檔名
return fileType.Split('|').Contains(ext.TrimStart('.')) ? ext : "";
}
private void Message(string msg, string url = null)
{
string script = url == null
? $"alert('{msg}');"
: $"alert('{msg}');window.location='{url}';";
HttpContext.Current.Response.Write($"<script>{script}</script>");
}
#endregion
}
}
// 支援日期格式化字串自動建立子資料夾
uploader.Path = "uploadimages/yyyy/MM/dd"; // 實際路徑:uploadimages/2024/01/15/
uploader.Path = "uploadimages/yyyyMMdd"; // 實際路徑:uploadimages/20240115/
uploader.Path = "uploadimages"; // 固定資料夾
單次呼叫 SaveAs(Request.Files) 自動遍歷所有上傳檔案,回傳以 | 分隔的路徑字串,適合批次上傳場景。
上傳成功自動產生 JavaScript 向父頁面 FCKeditor/CKEditor 寫入 <img> 標籤,實現「上傳即插入」體驗。
Path.GetFileName 移除目錄資訊)| 原始做法 | 建議改良 | 理由 |
|---|---|---|
| System.Drawing (GDI+) | ImageSharp / SkiaSharp | 跨平台、無記憶體洩漏、效能更佳 |
| Response.Write 輸出 Script | 回傳 JSON + 前端 AJAX 處理 | 關注點分離、易測試、支援 SPA 架構 |
| 儲存實體檔案 | Azure Blob / AWS S3 / MinIO | 擴展性、CDN 整合、容器化友善 |
| 硬編碼編輯器整合 | 事件/委派模式 | 解耦、支援多種編輯器 |
| 同步阻塞上傳 | 非同步 I/O + 進度回報 | 大檔案體驗、伺服器資源利用率 |
這個上傳元件展現了 ASP.NET WebForms 時代「類別庫封裝業務邏輯、頁面僅負責協調」的經典分層思想。核心優勢在於高度可配置的屬性設計、日期格式化路徑、編輯器整合、多檔批次處理。現代專案可保留核心邏輯(驗證、命名、路徑解析),將儲存介面抽象化、前端改為 AJAX、編輯器整合改為事件驅動,即可平滑遷移至 ASP.NET Core + 前端分離架構。