【问题标题】:How to validate/Check if an Image already exist in a DataBase?如何验证/检查图像是否已存在于数据库中?
【发布时间】:2018-09-15 13:52:51
【问题描述】:

我使用显示的代码来尝试验证我的图像。
不幸的是,我没有这样做,这是我第一次对图像进行验证。

我的逻辑有什么问题吗?我在这里忘记了什么吗?做这些的正确方法是什么。

注意:
我将图像保存为图像而不是路径,并且我的数据库中的数据类型为 Varbinary(MAX)

System.IO.FileNotFoundException: '找不到文件 'C:\Users\Andrea\source\repos\CapstoneSIMS\CapstoneSIMS\bin\Debug\72EF99A3668CF13820B113EB2E090C37716C9742'。'

(我在尝试插入图片时遇到这些错误)

public partial class ADDProduct : MetroForm
{
    SIMSProduct _view;

    public ADDProduct(SIMSProduct _view)
    {
        InitializeComponent();
        this._view = _view;                  
    }

    DataSet ds = new DataSet();
    DataTable dt = new DataTable();
    byte[] photobyte;

    public string CalculateHash(string filename)
    {
        SHA1CryptoServiceProvider crypt = new SHA1CryptoServiceProvider();
        //MD5CryptoServiceProvider crypt = new MD5CryptoServiceProvider();
        string hash = string.Empty;
        using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
        {
            byte[] checksum = crypt.ComputeHash(fs);
            foreach (byte b in checksum)
                hash += b.ToString("X2");
        }
        return (hash);
    }

    public static bool ImageExists(string filehash)
    {
        bool result = false;
        using (var connection = SQLConnection.GetConnection())
        {
            using (SqlCommand cmd = new SqlCommand("SELECT COUNT(0) FROM employee_product WHERE ImageHash = @ImageHash", connection))
            {
                connection.Open();
                int imagecount = (int)cmd.ExecuteScalar();
                result = imagecount == 0;
                connection.Close();
            }
        }
        return (result);
    }

    private void btn_add_Click(object sender, EventArgs e)
    {
        _view.ID = txt_id.Text;
        filehash = CalculateHash(@"C:\myimagefile.jpg");
        using (var con = SQLConnection.GetConnection())
        {
            if (string.IsNullOrEmpty(cbox_supplier.Text) || string.IsNullOrEmpty(txt_code.Text) || string.IsNullOrEmpty(txt_item.Text) || string.IsNullOrEmpty(txt_quantity.Text) || string.IsNullOrEmpty(txt_cost.Text) || pictureBox1.Image == null )
            {
                CustomNotifcation.Show("Please input the required fields", CustomNotifcation.AlertType.warning);
            }
            else
            {          
                ValidateCode.IsValidCode(txt_code,lbl_code,ds);
                string filehash = CalculateHash(pictureBox1.Tag.ToString());

                if (lbl_code.Visible == true)
                {
                    CustomNotifcation.Show("CODE ALREADY EXIST", CustomNotifcation.AlertType.error);
                    lbl_code.Visible = false;                      
                }
                else if (ImageExists(pictureBox1.Tag.ToString()))
                {
                    MessageBox.Show("image exists");
                    return;
                }
                else
                {
                    using (var select = new SqlCommand("Insert into employee_product (Image, ImageHash, ID, Supplier, Codeitem, Itemdescription, Date, Quantity, Unitcost) Values (@Image, @ID, @Supplier, @Codeitem, @Itemdescription, @Date, @Quantity, @Unitcost)", con))
                    {
                        var ms = new MemoryStream();
                        pictureBox1.Image.Save(ms, pictureBox1.Image.RawFormat);
                        photobyte = ms.GetBuffer();
                        select.Parameters.Add("@Image", SqlDbType.VarBinary).Value = photobyte;
                        select.Parameters.Add("@ImageHash", SqlDbType.VarChar, 50);
                        select.Parameters["@ImageHash"].Value = filehash;
                        select.Parameters.Add("@Supplier", SqlDbType.VarChar).Value = cbox_supplier.Text;
                        select.Parameters.Add("@Codeitem", SqlDbType.VarChar).Value = txt_code.Text.Trim();
                        select.Parameters.Add("@Itemdescription", SqlDbType.VarChar).Value = txt_item.Text.Trim();
                        select.Parameters.Add("@Date", SqlDbType.VarChar).Value = date;
                        select.Parameters.Add("@Quantity", SqlDbType.Int).Value = txt_quantity.Text.Trim();
                        select.Parameters.Add("@Unitcost", SqlDbType.Int).Value = txt_cost.Text.Trim();
                        select.ExecuteNonQuery();
                        CustomMessage.Show("Message: Item successfully added!",CustomMessage.Messagetype.Success);
                        pictureBox1.Image = null;
                        cbox_supplier.Items.Clear();
                        txt_code.Clear();
                        txt_item.Clear();
                        txt_quantity.Clear();
                        txt_cost.Clear();
                        _view.btn_update.Enabled = false;
                        _view.AddingProduct();
                        this.Close();
                    }
                }
            }
        }
    }

    private void pictureBox1_Click(object sender, EventArgs e)
    {
         var opnfd    = new OpenFileDialog();
        opnfd.Filter            = "Image Files (*.jpg;*.jpeg;.*.png;)|*.jpg;*.jpeg;.*.png;";
        opnfd.Title             = "Select Item";

        if (opnfd.ShowDialog() == DialogResult.OK)
        {
            pictureBox1.SizeMode    = PictureBoxSizeMode.StretchImage;
            pictureBox1.Image       = Image.FromFile(opnfd.FileName);
            CalculateHash(opnfd.FileName);
        }
    }
}


【问题讨论】:

    标签: c# sql-server winforms


    【解决方案1】:

    您可以计算图像的 MD5/SHA1 并将其存储在表中的单独列中,然后当您检查图像是否存在时,您可以计算当前图像的 MD5/SHA1,然后检查它是否匹配在数据库中。 MD5/SHA1 最多大约 50 个字符,并且对于每个图像应该是唯一的(已经观察到 MD5 冲突,我认为 SHA1 也有,但您不太可能将数百万个图像插入数据库)。

    您的表格如下所示:

    ID INT
    Image VARBINARY(MAX)
    ImageHash VARCHAR(50)
    

    加上您需要的任何其他列。

    要计算图像的 MD5/SHA1,您可以使用:

    public string CalculateHash(string filename)
    {
        SHA1CryptoServiceProvider crypt = new SHA1CryptoServiceProvider();
        //MD5CryptoServiceProvider crypt = new MD5CryptoServiceProvider();
        string hash = string.Empty;
        using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
        {
            byte[] checksum = crypt.ComputeHash(fs);
            foreach (byte b in checksum)
                hash += b.ToString("X2");
        }
    
        return(hash);
    }
    

    上述方法使用 SHA1 加密计算。如果您更喜欢使用 MD5(稍微快一点),请注释掉 SHA1 行并取消注释 MD5 行。

    Call it with:
    
    string filehash = CalculateHash(@"C:\myimagefile.jpg");
    

    然后,当您想检查您的图像是否已插入时,您可以使用查询:

    SELECT COUNT(0) FROM employee_product WHERE ImageHash = @ImageHash
    

    将查询参数设置为:

    cmd.Parameters.Add("@ImageHash", SqlDbType.Varchar, 50);
    cmd.Parameters["@ImageHash"].Value = filehash;
    

    然后调用:

    int result = (int)cmd.ExecuteScalar();
    if (result == 0)
    {
        // Insert your image, don't forget to insert the filehash into the ImageHash column or subsequent checks will always fail.
    }
    

    这是一种检查图像是否存在的更快方法,因为您只需将 50 个字符发送到数据库进行检查,而不是整个图像。

    因此,您将创建一个新方法来搜索数据库中是否存在图像:

    public bool ImageExists(string filehash)
    {
        bool result = false;
        using (SqlConnection connection = SQLConnection.GetConnection())
        {
            using (SqlCommand cmd = new SqlCommand("SELECT COUNT(0) FROM employee_product WHERE ImageHash = @ImageHash", connection))
            {
                connection.Open();
                int imagecount = (int)cmd.ExecuteScalar();
                result = imagecount == 0;
                connection.Close();
            }
        }
    
        return(result);
    }
    

    然后在这一行之前的 AddProduct 方法中:

    using (var select = new SqlCommand("Insert into employee_product (Image, ID, Supplier, Codeitem, Itemdescription, Date, Quantity, Unitcost) Values (@Image, @ID, @Supplier, @Codeitem, @Itemdescription, @Date, @Quantity, @Unitcost)", con))
    

    你可以插入:

    string filehash = CalculateHash(imagefilename);
    if (ImageExists(filehash)
    {
        MessageBox.Show("Image already exists");
        return;
    }
    
    // the rest of your code goes here including adding a parameter to take the ImageHash.
    

    当您从磁盘读取图像文件时(我假设这是您从中获取图像的位置),那么您可以将文件名存储在 PictureBox.Tag 对象中。然后从 PictureBox.Tag 中提取该文件名(包括路径)并将其传递给 ImageExists:

    string filehash = CalculateHash(pictureBox1.Tag.ToString());
    

    要将文件名放入图片框中,请使用:

    using(OpenFileDialog ofd = new OpenFileDialog())
    {
        if (ofd.ShowDialog() == DialogResult.OK)
        {
            pictureBox1.Image = Image.FromFile(ofd.FileName);
            pictureBox1.Tag = ofd.FileName;
        }
    }
    

    或者,您可以在用户加载图像时进行哈希计算并将其存储在标签中:

    using(OpenFileDialog ofd = new OpenFileDialog())
    {
        if (ofd.ShowDialog() == DialogResult.OK)
        {
            pictureBox1.Image = Image.FromFile(ofd.FileName);
            pictureBox1.Tag = CalculateHash(ofd.FileName);
        }
    }
    

    然后当你调用ImageExists 时使用:

    if (ImageExists(pictureBox1.Tag.ToString()))
    {
        MessageBox.Show("image exists");
        return;
    }
    

    【讨论】:

    • 上面的代码,我应该把它放在哪里?我的表格?还是我的课? string filehash = CalculateHash(@"C:\myimagefile.jpg"); // 还有这些文件名是什么?
    • CalculateHash 是一个方法,你可以把它放在任何你喜欢的地方。只要您可以在需要时访问它。您需要创建一个新方法来搜索文件哈希的查询和参数。我会更新我的答案。至于文件名,你说你正在上传一张图片,那张图片,我假设来自一个文件,所以 CalculateHash 方法需要一个文件名来计算哈希。
    • 是的,我遇到了错误,因为我正在使用 openfile 对话框将图像打开到我的计算机。我会更新我的代码看看它是否正确
    • 您可以将MD5/SHA1 哈希更改为SHA256。在这种情况下发生碰撞是不可能的(有点更大,但不需要格式化)。顺便说一句,很好的答案。
    • System.IO.FileNotFoundException: '找不到文件'C:\Users\Andrea\source\repos\CapstoneSIMS\CapstoneSIMS\bin\Debug\72EF99A3668CF13820B113EB2E090C37716C9742'。当我尝试插入图像时,我得到了这个。
    【解决方案2】:

    ExecuteNonQuery() 用于 INSERT、UPDATE、DELETE 和 DDL 语句,即不返回行的命令。您应该使用ExecuteReader() 并使用返回的阅读器来读取返回的数据,而不是使用SqlDataAdapter。或者,更好的是,使用类似

    的语句
    SELECT COUNT(*) FROM employee_product WHERE Image = @Image
    

    然后使用ExecuteScalar() 获取计数:

    using (var con = SQLConnection.GetConnection())
    using (var cmd = new SqlCommand(sql, con)) {
        cmd.Parameters.Add("@Image", SqlDbType.VarBinary).Value = PhotoByte;
        con.Open();
        int count = (int)cmd.ExecuteScalar();
        if (count > 0 ) {
            ...
        }
    }
    

    ExecuteScalar() 返回结果集第一行的第一列。

    但是将哈希码与图像一起存储并比较哈希而不是比较整个图像数据可能会更有效。哈希码甚至可以被索引以便更快地访问。

    【讨论】:

    • 谢谢你的澄清,如果能回答我的问题,我会试试这个
    • 使用COUNT 检查是否存在可能会对性能产生负面影响。最好使用简单的EXISTS 子句。
    • @Anonymous 想象一下当你有 10M 行但没有索引的场景。使用 COUNT(*) 您基本上必须扫描整个表,使用 EXISTS 您可以在找到第一次出现后停止。
    • 请给我一个参考,应该开始研究这些
    【解决方案3】:
    • 首先放置 VarBinary 的长度,因为没有特定长度的 VarBinary 具有默认长度
    • 第二件事改变 [ExecuteNonQuery] 并使用其他东西,如 [ExecuteReader] 或 [sqldataAdapter] 或 [SqlScalar]

       select.Parameters.Add("@Image", SqlDbType.VarBinary,[Length   VarBinary Column in database ]).Value = PhotoByte;
      

    祝你有美好的一天

    【讨论】:

      【解决方案4】:
      using (var con = SQLConnection.GetConnection())
      {
          using (var select = new SqlCommand(SELECT COUNT(0) FROM employee_product WHERE Image = @Image, con))
          {
              cmd.Parameters.Add("@Image", SqlDbType.VarBinary).Value = PhotoByte;
              int count = (int)select.ExecuteScalar();
              if (count > 0 ) 
              {
                  lbl.Show();
              }
          }
      }
      

      【讨论】:

      • 这个答案什么也没解释,基本上是我答案中代码的副本。请不要发布仅代码的答案并格式化您的代码。
      猜你喜欢
      • 2019-02-20
      • 2023-02-10
      • 1970-01-01
      • 1970-01-01
      • 2014-04-22
      • 1970-01-01
      • 2020-07-25
      • 1970-01-01
      • 2021-12-19
      相关资源
      最近更新 更多