首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C# ASP.NET 輪播圖管理:檔案上傳、刪除、重命名完整實作與 Bug 修復

     編輯:程式設計綜合問答

開發者實作輪播圖後台管理:上傳 JPG (1920×481)、列表顯示、刪除、自動重命名排序。發現刪除後重新讀取出錯。本文分析核心 Bug、提供修正代碼與架構優化建議。

核心 Bug 分析:刪除後雙重綁定衝突

protected void BtnDelete_Click(...) {
    // ... 刪除檔案 ...
    if (file.Exists == false) {
        bindReset(); // 重新命名:1.jpg, 2.jpg, 3.jpg...
        bind();      // ← 問題:bindReset 內部已隱含讀取目錄,這裡再 bind() 造成狀態不一致
    }
}

問題循環

  1. 刪除 2.jpg
  2. bindReset():讀取目錄 → 獲取 1.jpg, 3.jpg → 重命名為 1.jpg, 2.jpg
  3. bind():再次讀取目錄 → 獲取 1.jpg, 2.jpg → 建構 DataTable
  4. 若兩次讀取間隙有並發操作,或 bindReset 邏輯有瑕疵,導致 GridView 資料錯誤

修正版 BtnDelete_Click

protected void BtnDelete_Click(object sender, EventArgs e) {
    int row = ((GridViewRow)((Button)sender).NamingContainer).RowIndex;
    HiddenField HFid = (HiddenField)GV.Rows[row].Cells[0].FindControl("HFid");
    string Route = SlideFolderPath + HFid.Value + ".jpg";
    
    FileInfo file = new FileInfo(Route);
    if (file.Exists) {
        file.Attributes = FileAttributes.Normal;
        file.Delete();
        
        if (!file.Exists) {
            // 僅呼叫 bindReset(),內部已包含重新讀取與綁定
            bindReset(); 
            // 移除這行:bind(); 
            ClientScript.RegisterStartupScript(GetType(), "ok", "alert('刪除成功');", true);
        } else {
            ClientScript.RegisterStartupScript(GetType(), "err", "alert('刪除失敗');", true);
        }
    }
}

bindReset() 邏輯優化

public void bindReset() {
    // 1. 讀取現有檔案,按檔名排序 (確保順序確定)
    var files = Directory.GetFiles(SlideFolderPath, "*.jpg")
                         .Select(f => new FileInfo(f))
                         .OrderBy(f => f.Name, StringComparer.OrdinalIgnoreCase)
                         .ToList();
    
    // 2. 重新命名:1.jpg, 2.jpg, 3.jpg...
    for (int i = 0; i < files.Count; i++) {
        string newName = (i + 1) + ".jpg";
        string newPath = Path.Combine(SlideFolderPath, newName);
        if (files[i].FullName != newPath) {
            files[i].MoveTo(newPath); // 覆蓋目標若存在
        }
    }
    
    // 3. 直接重新綁定 GridView (不再獨立呼叫 bind())
    RebindGridView();
}

private void RebindGridView() {
    var files = Directory.GetFiles(SlideFolderPath, "*.jpg")
                         .Select(f => new FileInfo(f))
                         .OrderBy(f => f.Name)
                         .ToList();
    
    DataTable dt = new DataTable();
    dt.Columns.Add("URL");
    dt.Columns.Add("id");
    
    foreach (var f in files) {
        string name = Path.GetFileNameWithoutExtension(f.Name);
        string url = MyConfig.WebImgUrl + "/kuaigou/huandeng/" + f.Name;
        dt.Rows.Add(url, name);
    }
    
    LblNumber.Text = dt.Rows.Count.ToString();
    GV.DataSource = dt;
    GV.DataBind();
    
    divNumber.Style["display"] = dt.Rows.Count > 0 ? "block" : "none";
    
    if (GV.Rows.Count == 1) {
        ((Button)GV.Rows[0].Cells[1].FindControl("BtnDelete")).Enabled = false;
    }
}

架構優化建議

1. 分離關注點:檔案服務類

public class SlideImageService {
    private readonly string _folderPath;
    private readonly string _webUrlPrefix;
    
    public SlideImageService(string folderPath, string webUrlPrefix) {
        _folderPath = folderPath;
        _webUrlPrefix = webUrlPrefix;
    }
    
    public List GetAll() { /* 讀取目錄建構 DTO */ }
    public void Add(Stream stream, string fileName) { /* 驗證尺寸、儲存 */ }
    public void Delete(int id) { /* 刪除檔案 + 重新排序 */ }
    public void Reorder() { /* 統一重新命名邏輯 */ }
}

public class SlideImage {
    public int Id { get; set; }
    public string Url { get; set; }
    public string FileName { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
}

2. 使用資料庫記錄中繼資料 (生產環境必備)

CREATE TABLE slide_images (
    id INT PRIMARY KEY AUTO_INCREMENT,
    file_name VARCHAR(100) NOT NULL,
    display_order INT NOT NULL,
    width INT, height INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 刪除時:DELETE FROM ...; UPDATE ... SET display_order = display_order - 1 WHERE display_order > @deletedOrder;
-- 避免檔案系統重命名競態、支援軟刪除、記錄操作日誌

3. 上傳驗證強化

// 檢查 Content-Type 而非僅副檔名
if (FUDtal.PostedFile.ContentType != "image/jpeg") { /* 拒絕 */ }

// 使用 Image.FromStream 驗證真實尺寸
using (var img = Image.FromStream(FUDtal.PostedFile.InputStream)) {
    if (img.Width != 1920 || img.Height != 481) { /* 拒絕 */ }
    // 重要:FromStream 後需重設 Position 或重新讀取流才能 SaveAs
    FUDtal.PostedFile.InputStream.Position = 0;
    FUDtal.PostedFile.SaveAs(path);
}

總結

核心 Bug 在於 BtnDelete_Click 中 **刪除成功後同時呼叫 bindReset() 和 bind() 造成雙重綁定衝突**。修正為**僅呼叫 bindReset()**,並將讀取、重命名、綁定邏輯整合為單一 RebindGridView() 方法。長期建議引入服務層封裝檔案操作、資料庫記錄中繼資料、強化上傳驗證,構建可維護、可擴展的輪播圖管理系統。

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