简体   繁体   中英

How to validate/Check if an Image already exist in a DataBase?

I use the code shown to try to validate my image.
Unfortunately, I failed to do that and this is the first time I'm doing a validation for image.

Is there anything wrong in my logic? Did I forgot something here? What's the right way in doing these.

NOTE:
I'm saving the image as image not path and also the datatype is Varbinary(MAX) in my database.

System.IO.FileNotFoundException: 'Could not find file 'C:\\Users\\Andrea\\source\\repos\\CapstoneSIMS\\CapstoneSIMS\\bin\\Debug\\72EF99A3668CF13820B113EB2E090C37716C9742'.'

(I'm getting these error when i tried to insert image)

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);
        }
    }
}


我的数据库

ExecuteNonQuery() is for INSERT, UPDATE, DELETE and DDL-statements, ie, commands not returning rows. You should use ExecuteReader() and use the returned reader to read the data returned instead of using a SqlDataAdapter . Or, even better, use a statement like

SELECT COUNT(*) FROM employee_product WHERE Image = @Image

And then get the count with 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() returns the first column of the first row of the result set.

But it would probably be more efficient to store a hash code together with the image and compare the hash instead of comparing the whole image data. The hash code could even be indexed for faster access.

You could calculate the MD5/SHA1 of the image and store this in a separate column in your table, then when you come to check if the image exists you calculate the MD5/SHA1 of the current image and then check that it matches in the database. The MD5/SHA1 is going to be around 50 characters max and should be unique for each image (MD5 collisions have been observed and I think SHA1 have also, but you're unlikely to be inserting millions of images into your DB).

Your table would look like:

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

plus whatever other columns you need.

To calculate the MD5/SHA1 for the image you would use this:

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);
}

The above method uses the SHA1 crypto calculation. If you'd prefer to use MD5 (slightly faster) then comment out the SHA1 line and uncomment the MD5 line.

Call it with:

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

Then when you want to check if your image is already inserted you can use the query:

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

Set the parameter of your query to:

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

Then call:

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.
}

This is a much quicker way of checking that the image exists as you are only sending 50 characters to the database for checking rather than an entire image.

So you'd create a new method to search the database for the existance of the image:

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);
}

Then in your AddProduct method just before this line:

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))

You can insert:

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.

When you read in the image file from disk (I am assuming that is where you are getting the image from) then you can store the filename in the PictureBox.Tag object. Then pull that filename (including path) from the PictureBox.Tag and pass it through to ImageExists:

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

To put the filename in the picturebox use:

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

Alternately you can do the hash calculation when the user loads in the image and store that in the Tag instead:

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

Then when you call ImageExists use:

if (ImageExists(pictureBox1.Tag.ToString()))
{
    MessageBox.Show("image exists");
    return;
}
  • First Thing Put The Length Of VarBinary Because VarBinary Without Specific Length Has Default Length
  • Second Thing Change The [ExecuteNonQuery] and use other thing like [ExecuteReader] or [sqldataAdapter] or [SqlScalar]

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

Have a Nice Day

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();
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM