iT邦幫忙

2021 iThome 鐵人賽

DAY 12
1
Modern Web

ASP.NET Web Forms 入門 - 30天建立遊艇網頁專案後端及後台功能 C#系列 第 12

Day 12 - Using List<T> to Store JSON Format Path with ASP.NET Web Forms C# 用強類型物件清單儲存 JSON 格式的相簿圖片路徑

=x= 🌵 建立後台相簿管理並使用 JSON 格式儲存多個圖片的路徑。


相簿管理功能介紹 :

📌 這個頁面的作法是自己想出來的方式,結合想到的畫面跟目前會使用的技能,可能並非常規見到的作法,主要是在思考 C# 陣列無法在宣告後變更長度,但是由於需要一個可以增刪,用於儲存圖片名稱的功能,在查找資料時查到可以用 List<T> 來存 JSON 資料,於是想辦法做出想要的功能,大致的介面可以參考下圖。

https://ithelp.ithome.com.tw/upload/images/20210926/20139487eYxStl23sd.jpg



相簿管理功能實作 :

1. 建立資料庫中圖片名稱 JSON 格式的資料表設定如下,預設值設成 ('[]')

https://ithelp.ithome.com.tw/upload/images/20210926/20139487V3b4FFGdmm.jpg

  • 🌵 如果沒設預設值,程式邏輯判斷時需額外處理遇到 Null 時的狀態。


2. 於專案使用 Nuget 安裝 NetVips 及 NetVips.Native 套件 (用於壓縮圖檔)

https://ithelp.ithome.com.tw/upload/images/20210926/20139487gbttQLjqpU.jpg


3. 於 .asp 頁面排版用到的控制項如下

<h6>Upload Horizontal Group Image :</h6>
<div class="input-group my-3">
    <asp:FileUpload ID="imageUploadH" runat="server" class="btn btn-outline-primary btn-block" AllowMultiple="True" />
    <asp:Button ID="UploadHBtn" runat="server" Text="Upload" class="btn btn-primary" OnClick="UploadHBtn_Click" />
</div>
<h6>Horizontal Image List :</h6>
<asp:RadioButtonList ID="RadioButtonListH" runat="server" class="my-3 mx-auto" AutoPostBack="True" OnSelectedIndexChanged="RadioButtonListH_SelectedIndexChanged" CellPadding="10" RepeatColumns="2" RepeatDirection="Horizontal"></asp:RadioButtonList>
<asp:Button ID="DelHImageBtn" runat="server" Text="Delete Image" type="button" class="btn btn-danger btn-sm" OnClientClick="return confirm('Are you sure you want to delete?')" Visible="False" OnClick="DelHImageBtn_Click" />
  • 🌵 FileUpload 控制項的 AllowMultiple 設為 "True" 可以選擇複數檔案。

  • 🌵 RadioButtonList 控制項的 RepeatDirection 可設定選項排版為直式或橫式。

  • 🌵 RadioButtonList 控制項的 RepeatColumns 可設定超過幾個選項就進行換行。


4. 於 .asp 頁面後製程式碼 .cs 加入圖片名稱 JSON 格式

// JSON 資料 Horizontal Image
public class ImageNameH
{
    public string SaveName { get; set; }
}


5. 宣告全域 List<T> 用於儲存圖片名稱,並於 Page_Load 事件使用 loadImageHList(); 方法

//宣告全域 List<T> 可用 Add 依序添加資料
private List<ImageNameH> saveNameListH = new List<ImageNameH>();

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack) {
        loadImageHList();
    }
}


6. 建立 loadImageHList() 方法程式邏輯如下

private void loadImageHList()
{
    //連線資料庫取出資料
    SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
    string sqlLoad = "SELECT certificatHorizontalImgJSON FROM Company WHERE id = 1";
    SqlCommand command = new SqlCommand(sqlLoad, connection);
    connection.Open();
    SqlDataReader reader = command.ExecuteReader();
    if (reader.Read()) {
        string loadJson = reader["certificatHorizontalImgJSON"].ToString();
        //反序列化JSON格式
        saveNameListH = JsonConvert.DeserializeObject<List<ImageNameH>>(loadJson);
    }
    connection.Close();
    //可以改成用 ?.Count 來判斷不是 Null 後才執行 .Count 避免錯誤
    if (saveNameListH?.Count > 0) {
        //逐一取出 JSON 的每筆資料
        foreach (var item in saveNameListH) {
            //將 RadioButtonList 選項內容改為圖片格式,值設為檔案名稱
            ListItem listItem = new ListItem($"<img src='/Tayanahtml/images/{item.SaveName}' alt='thumbnail' class='img-thumbnail' width='230px'/>", item.SaveName);
            //加入圖片選項
            RadioButtonListH.Items.Add(listItem);
        }
    }
        DelHImageBtn.Visible = false; //刪除鈕有選擇圖片時才顯示
}


7. 建立 Upload 按鈕的 OnClick 事件程式邏輯如下

protected void UploadVBtn_Click(object sender, EventArgs e)
{
    //有選擇檔案才執行
    if (imageUploadV.HasFile) {
        //先讀取資料庫原有資料
        loadImageVList();
        string savePath = Server.MapPath("~/Tayanahtml/images/");

        //添加圖檔資料
        //逐一讀取選擇的圖片檔案
        foreach (HttpPostedFile postedFile in imageUploadV.PostedFiles) {
            //儲存圖片檔案及圖片名稱
            //檢查專案資料夾內有無同名檔案,有同名就加流水號
            DirectoryInfo directoryInfo = new DirectoryInfo(savePath);
            string fileName = postedFile.FileName;
            string[] fileNameArr = fileName.Split('.');
            int count = 0;
            foreach (var fileItem in directoryInfo.GetFiles()) {
                if (fileItem.Name.Contains(fileNameArr[0])) {
                    count++;
                }
            }
            fileName = fileNameArr[0] + $"({count + 1})." + fileNameArr[1];
            //在圖片名稱前加入 temp 標示並儲存圖片檔案
            postedFile.SaveAs(savePath + "temp" + fileName);
            //新增 JSON 資料
            saveNameListV.Add(new ImageNameV { SaveName = fileName });

            //使用 NetVips 套件進行壓縮圖檔
            //判斷儲存的原始圖片寬度是否大於設定寬度的 2 倍
            var img = NetVips.Image.NewFromFile(savePath + "temp" + fileName);
            if (img.Width > 214 * 2) {
                //產生原使圖片一半大小的新圖片
                var newImg = img.Resize(0.5);
                //如果新圖片寬度還是大於原始圖片設定寬度的 2 倍就持續縮減
                while (newImg.Width > 214 * 2) {
                    newImg = newImg.Resize(0.5);
                }
                //儲存正式名稱的新圖片
                newImg.WriteToFile(savePath + fileName);
            }
            else {
                postedFile.SaveAs(savePath + fileName);
            }
            //刪除原始圖片
            File.Delete(savePath + "temp" + fileName);
        }

        //更新新增後的圖片名稱 JSON 存入資料庫
        string fileNameJsonStr = JsonConvert.SerializeObject(saveNameListV);
        SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
        string sql = "UPDATE Company SET certificatVerticalImgJSON = @fileNameJsonStr WHERE id = 1";
        SqlCommand command = new SqlCommand(sql, connection);
        command.Parameters.AddWithValue("@fileNameJsonStr", fileNameJsonStr);
        connection.Open();
        command.ExecuteNonQuery();
        connection.Close();

        //渲染畫面
        RadioButtonListV.Items.Clear();
        loadImageVList();
    }
}


8. 建立 RadioButtonList 選取改變時 OnSelectedIndexChanged 事件程式邏輯如下

protected void RadioButtonListH_SelectedIndexChanged(object sender, EventArgs e)
{
    //顯示刪除按鈕
    DelHImageBtn.Visible = true;
}
  • 🌵 在 .aspx 頁面最上方設定加入MaintainScrollPositionOnPostback="True" 可以讓畫面刷新後維持在原位置,而不會跑到最上方。


9. 建立 Delete Image 按鈕的 OnClick 事件程式邏輯如下

protected void DelHImageBtn_Click(object sender, EventArgs e)
{
    //先讀取資料庫原有資料
    loadImageHList();
    //取得選取項目的值
    string selHImageStr = RadioButtonListH.SelectedValue;

    //刪除圖片檔案
    string savePath = Server.MapPath("~/Tayanahtml/images/");
    File.Delete(savePath + selHImageStr);

    //逐一比對原始資料 List<saveNameListH> 中的檔案名稱
    for (int i = 0; i < saveNameListH.Count; i++) {
        //與刪除的選項相同名稱
        if (saveNameListH[i].SaveName.Equals(selHImageStr)) {
            //移除 List 中同名的資料
            saveNameListH.RemoveAt(i);
        }
    }

    //更新刪除後的圖片名稱 JSON 存入資料庫
    SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
    string saveNameJsonStr = JsonConvert.SerializeObject(saveNameListH);
    string sql = "UPDATE Company SET certificatHorizontalImgJSON = @saveNameJsonStr WHERE id = 1";
    SqlCommand command = new SqlCommand(sql, connection);
    command.Parameters.AddWithValue("@saveNameJsonStr", saveNameJsonStr);
    connection.Open();
    command.ExecuteNonQuery();
    connection.Close();

    //渲染畫面
    RadioButtonListH.Items.Clear();
    loadImageHList();
}


10. 模擬頁面測試功能是否正常,完成~



本日總結 :

📢 製作時 List<T> 會需要放到全域的原因,是因為 PostBack 時資料會被清掉,而其它方法也會需要用到,所以放到該頁後置程式碼的全域,並且把讀資料庫資料這個方法獨立出來,在進行其它關聯事件時會先去讀資料,這樣才不會只存到新的資料,舊的資料反而被洗掉,而壓縮圖片應該算本頁的彩蛋,壓縮的方法是自己想的,可能有更高效的作法,目前圖片存法最大會接近需求的 2 倍,另外判斷有無同名檔案的方法,一開始是做跟資料庫比對,但發現這樣邏輯不對,因為如果其它頁面有上傳同名檔案,這樣原本該頁用的圖片就會被蓋掉,應該直接比對放檔案的資料夾才對。

  • 明日將介紹製作 Company Manager - Content Page 後台的相關細節。

上一篇
Day 11 - Using CKEditor + CKFinder WYSIWYG Editor with ASP.NET Web Forms C# 在網頁嵌入 WYSIWYG 文字編輯器
下一篇
Day 13 - 依 COMPANY 前台頁面分析拆解後,逐步建立後台功能 - 文字編輯器及相簿應用 - ASP.NET Web Forms C#
系列文
ASP.NET Web Forms 入門 - 30天建立遊艇網頁專案後端及後台功能 C#30

尚未有邦友留言

立即登入留言