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 後台的相关细节。

<<:  第五章之二

>>:  [Day22] Rust 直接使用资料库语法操作资料库 (Part1)

用React刻自己的投资Dashboard Day16 - react-router-dom让SPA也有路由

tags: 2021铁人赛 React 前一篇提到的导览列的各个按钮,点击之後会跳到不同的页面,每个...

Day30 - 铁人赛永丰APIs实战30天,跪着都要完赛。

终於到了第30天了,对於第一次参加iThome铁人赛的我而言觉得有点不真实呀,可以发表这篇文章有和想...

[履历]你要对自己的履历了若指掌

如果为了履历好看放上参与度不高的专案、不够熟悉的技术、不真实的自我介绍;遇上老练的面试官,你将会血...

Day22 跟着 spinlock 旋转吧

前言 昨天讲完了最基础的 atomic的资讯,了解了 atomic可以保护某个变数的资料正确性,当有...

Wrapping up

终於来到最後一篇了!不经不觉已经写了三十篇文章。我们由 Ktor client 接驳 API 一直讲...