C# saving images to MySql database as blob - c#

For some reason my code fails when I try to update the image for a user. The image is not saved properly. For instance an image of 38 kib is saved as 13 bytes in the database.
This is my code:
public void UploadImage(Image img)
{
OpenConnection();
MySqlCommand command = new MySqlCommand("", conn);
command.CommandText = "UPDATE User SET UserImage = '#UserImage' WHERE UserID = '" + UserID.globalUserID + "';";
byte[] data = imageToByte(img);
MySqlParameter blob = new MySqlParameter("#UserImage", MySqlDbType.Blob, data.Length);
blob.Value = data;
command.Parameters.Add(blob);
command.ExecuteNonQuery();
CloseConnection();
}
public byte[] imageToByte(Image img)
{
using (var ms = new MemoryStream())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms.ToArray();
}
}
OpenConnection and closeconnection are simply conn.Open() and conn.Close().
The conversion however doesn't fail:
But in the database I see this:
Does anyone have any idea what is going on here?

Replace this code:
OpenConnection();
MySqlCommand command = new MySqlCommand("", conn);
command.CommandText = "UPDATE User SET UserImage = '#UserImage' WHERE UserID = '" + UserID.globalUserID + "';";
byte[] data = imageToByte(img);
MySqlParameter blob = new MySqlParameter("#UserImage", MySqlDbType.Blob, data.Length);
blob.Value = data;
command.Parameters.Add(blob);
command.ExecuteNonQuery();
CloseConnection();
With
var userImage = imageToByte(img);
OpenConnection();
var command = new MySqlCommand("", conn);
command.CommandText = "UPDATE User SET UserImage = #userImage WHERE UserID = #userId;";
var paramUserImage = new MySqlParameter("#userImage", MySqlDbType.Blob, userImage.Length);
var paramUserId = new MySqlParameter("#userId", MySqlDbType.VarChar, 256);
paramUserImage.Value = userImage;
paramUserId.Value = UserID.globalUserID;
command.Parameters.Add(paramUserImage);
command.Parameters.Add(paramUserId);
command.ExecuteNonQuery();
CloseConnection();
You were sending '#UserImage' which is a 10 byte long string, remove the quotes and it should work.
Above code also uses parameters for both of your variables which you should always do.
Either way hope this helps you.

At first, i like to save blobs in the database good one :)
If you pass a parameter, do not encapsulate it in ', because than ADO.Net/MySql will not recognize it as a parameter, rather than a string:
command.CommandText = "UPDATE User SET UserImage = #UserImage WHERE UserID = '" + UserID.globalUserID + "';";
If you start using parameter, why not pass UserID also as a parameter:
command.CommandText = "UPDATE User SET UserImage = #UserImage WHERE UserID = #userId;";
This would make every think much clearer.
One important thing: If you store blobs in the database, never use select * from ..., because often you don't want to retrieve the blob, but you will with the star. This cause unnecessary trafic and decrease the performance.
Hope this helps!

Related

C# MySQL save File in DB [duplicate]

For some reason my code fails when I try to update the image for a user. The image is not saved properly. For instance an image of 38 kib is saved as 13 bytes in the database.
This is my code:
public void UploadImage(Image img)
{
OpenConnection();
MySqlCommand command = new MySqlCommand("", conn);
command.CommandText = "UPDATE User SET UserImage = '#UserImage' WHERE UserID = '" + UserID.globalUserID + "';";
byte[] data = imageToByte(img);
MySqlParameter blob = new MySqlParameter("#UserImage", MySqlDbType.Blob, data.Length);
blob.Value = data;
command.Parameters.Add(blob);
command.ExecuteNonQuery();
CloseConnection();
}
public byte[] imageToByte(Image img)
{
using (var ms = new MemoryStream())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms.ToArray();
}
}
OpenConnection and closeconnection are simply conn.Open() and conn.Close().
The conversion however doesn't fail:
But in the database I see this:
Does anyone have any idea what is going on here?
Replace this code:
OpenConnection();
MySqlCommand command = new MySqlCommand("", conn);
command.CommandText = "UPDATE User SET UserImage = '#UserImage' WHERE UserID = '" + UserID.globalUserID + "';";
byte[] data = imageToByte(img);
MySqlParameter blob = new MySqlParameter("#UserImage", MySqlDbType.Blob, data.Length);
blob.Value = data;
command.Parameters.Add(blob);
command.ExecuteNonQuery();
CloseConnection();
With
var userImage = imageToByte(img);
OpenConnection();
var command = new MySqlCommand("", conn);
command.CommandText = "UPDATE User SET UserImage = #userImage WHERE UserID = #userId;";
var paramUserImage = new MySqlParameter("#userImage", MySqlDbType.Blob, userImage.Length);
var paramUserId = new MySqlParameter("#userId", MySqlDbType.VarChar, 256);
paramUserImage.Value = userImage;
paramUserId.Value = UserID.globalUserID;
command.Parameters.Add(paramUserImage);
command.Parameters.Add(paramUserId);
command.ExecuteNonQuery();
CloseConnection();
You were sending '#UserImage' which is a 10 byte long string, remove the quotes and it should work.
Above code also uses parameters for both of your variables which you should always do.
Either way hope this helps you.
At first, i like to save blobs in the database good one :)
If you pass a parameter, do not encapsulate it in ', because than ADO.Net/MySql will not recognize it as a parameter, rather than a string:
command.CommandText = "UPDATE User SET UserImage = #UserImage WHERE UserID = '" + UserID.globalUserID + "';";
If you start using parameter, why not pass UserID also as a parameter:
command.CommandText = "UPDATE User SET UserImage = #UserImage WHERE UserID = #userId;";
This would make every think much clearer.
One important thing: If you store blobs in the database, never use select * from ..., because often you don't want to retrieve the blob, but you will with the star. This cause unnecessary trafic and decrease the performance.
Hope this helps!

Sql reader returning DBnull for every row

I am trying to display pictures from my SQLSEVER database on my website. My users have a picture field with a picture datatype. If the picture column is null, then I want the picture displayed to be a egg.jpg, but right now for every person their picture is egg.jpg, even if they have a picture in the database. Here is my method.
public string getImageUrl()
{
System.Data.SqlClient.SqlConnection sc = new System.Data.SqlClient.SqlConnection();
sc.ConnectionString = "Server =MRCOMPUTER2\\SQLEXPRESS; Database = WBL;Trusted_Connection=Yes;";
sc.Open();
System.Data.SqlClient.SqlCommand insert = new System.Data.SqlClient.SqlCommand();
insert.Connection = sc;
insert.CommandText = "SELECT profilePicture from SystemUser";
insert.ExecuteNonQuery();
SqlDataReader reader = insert.ExecuteReader();
string url = "";
while (reader.Read())
{
if ( !DBNull.Value.Equals(reader[0]))
{
url = "data:Image / png; base64," + Convert.ToBase64String((byte[])reader[0]);
}
else {
url = "images/egg.jpg";
}
}
return url;
}
Your code returns the image for the last user in your table.
Here:
insert.CommandText = "SELECT profilePicture from SystemUser";
you select all users from the table (not just the one you currently show). Then:
while (reader.Read())
{
...
url = ...
...
}
you re-assign url inside every iteration of your while loop. This is semantically equivalent to:
url = ... /* The value determined from the last record of the reader. */
Thus, all your users show the same image - the one of the last user in your table.
You SELECT statement is attached into a ExecuteNonQuery?
Take it off.
Just perform the READER statement...
Try this:
static void Main(string[] args)
{
string connectionString = "Server=.;Database=AA;Trusted_Connection=True;";
/*
CREATE TABLE [dbo].[SystemUser]
(
[ProfilePicture] [varbinary](max) NULL
)
*/
using (SqlConnection connection = new SqlConnection(connectionString))
{
string sql = #"
INSERT [AA].[dbo].[SystemUser] ([ProfilePicture]) VALUES (#ProfilePicture);
INSERT [AA].[dbo].[SystemUser] ([ProfilePicture]) VALUES (NULL);
";
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = sql;
command.CommandType = CommandType.Text;
byte[] bytes = File.ReadAllBytes(#"1.jpg");
command.Parameters.AddWithValue("#ProfilePicture", bytes);
connection.Open();
command.ExecuteNonQuery();
}
DataSet ds = new DataSet();
using (SqlConnection connection = new SqlConnection(connectionString))
{
string sql = #"
SELECT TOP 1000 [ProfilePicture] FROM [AA].[dbo].[SystemUser];
";
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = sql;
command.CommandType = CommandType.Text;
connection.Open();
SqlDataAdapter da = new SqlDataAdapter(command);
da.Fill(ds);
}
var rows = ds.Tables[0].Rows.Cast<DataRow>();
foreach (DataRow row in rows)
{
byte[] bytes = row.Field<byte[]>(0);
if (bytes != null)
{
string fileName = Guid.NewGuid().ToString("N") + ".jpg";
File.WriteAllBytes(fileName, bytes);
}
}
}
Can you try using the name of the column such as
var Val = (String)reader["column name"];
Also, try something like this to test:
while (reader.Read())
{
var testVal = reader.GetString(0);
Var testVal2 = reader.GetString(1);

Retrieve Image to a picturebox from access database

I am trying to retrieve image from access database by using listbox select event
I used the following code to save data to the database :
command.CommandText = "insert into EmployeeInfo (FirstName,LastName,Pay,Pic) values ('" + txt_fname.Text + "' , '" + txt_lname.Text + "' , '" + txt_pay.Text + "' , #productpic)";
MemoryStream stream = new MemoryStream();
pb1.Image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] pic = stream.ToArray();
command.Parameters.AddWithValue("#productpic", pic);
And I am retrieving data using the following code :
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
string query = "select * from EmployeeInfo where FirstName = '" + listBox1.Text +"'";
command.CommandText = query;
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read()) {
list_fname.Text = reader["FirstName"].ToString();
list_lname.Text = reader["LastName"].ToString();
list_dob.Text = reader["DoB"].ToString();
}
connection.Close();
}
Now what I am trying to do is that to retrieve the picture associated with every row , field name is Pic into a picturebox , and show it in the while loop with other data . I have seen several questions asked by others but everyone is using datagridview to retrieve data where in my case i am trying to do it inside listbox select event .
Any kind of help would be highly appreciated . Thanks in advance .
Called this function in the while loop in listbox select event :
void loadpicture()
{
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
command.CommandText = "select pic from EmployeeInfo where FirstName = '" + listBox1.Text + "'";
OleDbDataAdapter da = new OleDbDataAdapter(command);
DataSet ds = new DataSet();
da.Fill(ds);
byte[] content = (byte[])ds.Tables[0].Rows[0].ItemArray[0];
MemoryStream stream = new MemoryStream(content);
pb1.Image = Image.FromStream(stream);
}

My code gives me and error called "There is already an open DataReader associated with this Command which must be closed first"

SqlConnection conn = new SqlConnection();
conn.ConnectionString = "Data Source=PROMOD-PC;Initial Catalog=travel_Directions;Integrated Security=True";
String Strt_Address = TextBox1.Text;
String End_Address = TextBox2.Text;
String filePath = FileUpload1.PostedFile.FileName;
String filename = Path.GetFileName(filePath);
String ExtStr = Path.GetExtension(filename);
String contenttype = String.Empty;
switch (ExtStr)
{
case ".png":
contenttype = "image/png";
break;
case ".jpg":
contenttype = "image/jpg";
break;
case ".gif":
contenttype = "image/gif";
break;
}
if (contenttype != string.Empty)
{
Stream Strmf = FileUpload1.PostedFile.InputStream;
BinaryReader br = new BinaryReader(Strmf);
Byte[] imgbytes = br.ReadBytes((Int32)Strmf.Length);
//SqlDataReader sqldread = new SqlDataReader();
String selQuery = "SELECT Id FROM MapDataImage WHERE Source='" + TextBox1.Text + "';";
{
SqlCommand scmd = new SqlCommand(selQuery, conn);
conn.Open();
SqlDataReader sqldread = scmd.ExecuteReader();
while (sqldread.Read())
{
int Dbid = (int)sqldread["Id"];
//string DbId = sqldread.GetInt32("Id").ToString();
Label4.Text = Convert.ToString(Dbid);
String QueryStr = "INSERT INTO User_Images(Id,Image) VALUES ('" + Dbid + "',#Image)";
SqlCommand scmd1 = new SqlCommand(QueryStr, conn);
scmd1.Parameters.Add("#Image", SqlDbType.VarBinary).Value = imgbytes;
scmd1.ExecuteNonQuery();
}
//String QueryStr = "UPDATE MapDataImage SET Image = #Image WHERE Source='" + TextBox1.Text + "';";
//SqlCommand scmd = new SqlCommand(QueryStr, conn);
//scmd.Parameters.Add("#Image", SqlDbType.VarBinary).Value = imgbytes;
sqldread.Close();
conn.Close();
}
}
first if condition will convert to bytes which i uploaded file through the file upload control.
Select command will check the user textbox input name equals with database value
..
if it's equal the code will get the id from that table and store it as a int variable.
my uploaded image and that id will goes to another table.
my first SQL table =
Id = int
Source = varchar(max)
my second SQL data table =
image_id = int (primary key) Auto increment
Id = int (first table id store in here)
image = (varbinary)MAX
scenario is....
ex- think Facebook, if Facebook account name is valid, we can upload unlimited photos.
so situation is same here. if my name is equal with data base it will retrieve its id and under that id it will insert the images to second data table
when i want to retrieve those images. i can use for loop retrieve the images from second table.. yeah?
Try to use a new connection instead in the sqldread.Read()
SqlConnection conn2 = new SqlConnection();
conn2.ConnectionString = ""; //your connection string
SqlCommand scmd1 = new SqlCommand(QueryStr, conn2);
scmd1.Parameters.Add("#Image", SqlDbType.VarBinary).Value = imgbytes;
conn2.Open();
scmd1.ExecuteNonQuery();
conn2.Close();
You can not reuse the connection in the loop while you are still reading from the SqlDataReader.
Two options:
Read all the ids and save them in a temporary list. Then loop through with this list.
Create a new connection to do the image insert.
You need to close the SqlDataReader object right after you get the Id value from the database and re-associate the reader with the next command object, like this:
while (sqldread.Read())
{
int Dbid = (int)sqldread["Id"];
}
sqldread.Close();
Label4.Text = Convert.ToString(Dbid);
String QueryStr = "INSERT INTO User_Images(Id,Image) VALUES ('" + Dbid + "',#Image)";
SqlCommand scmd1 = new SqlCommand(QueryStr, conn);
scmd1.Parameters.Add("#Image", SqlDbType.VarBinary).Value = imgbytes;
SqlDataReader sqldread = scmd1.ExecuteNonQuery();
Note: You should also look into the using statement, which allows for the database connection to be correctly closed on success or error (read: exception) in your logic.
You will need another connection object. You can use use you connection if one command is all ready associate with it.
So what you can do is create another object of the command and use it.
Other wise change your logic as follows
crate an array while reading
ArrayList ar=new ArrayList();
while (sqldread.Read())
{
int Dbid = (int)sqldread["Id"];
ar.add(Dbid);
}
Dispose the command
and use you update command using for loop on arraylist.
for(int i=0;i<ar.length;i++)
{
//write update statement here.
}

Image from SQL Server Table to asp.net image control

SqlCommand cmd = new SqlCommand("select top 1(CMTID),COMMENT,HEADING from FTAB ORDER BY CMTID DESC", conn);
conn.Open();
SqlDataReader sdr = cmd.ExecuteReader();
if (sdr.Read() == true)
{
lblHead.Text = sdr["HEADING"].ToString();
lblData.Text = sdr["COMMENT"].ToString();
}
conn.Close();
That is the code for normal data value retrieve from table now I want to get picture from SQL Server that is saved as Binary data that code mention in bottom. So I want to retrieve picture into an Image control of asp.net; please guide me.
if (FileUpload1.HasFile)
{
conn.Close();
String SqlQery;
SqlQery = "select max(CMTID) from FTAB";
SqlCommand cmdid = new SqlCommand(SqlQery, conn);
conn.Open();
MaxID = (int)(cmdid.ExecuteScalar()) + 1;
conn.Close();
byte[] img = new byte[FileUpload1.PostedFile.ContentLength];
HttpPostedFile myimg = FileUpload1.PostedFile;
myimg.InputStream.Read(img, 0, FileUpload1.PostedFile.ContentLength);
SqlCommand cmd = new SqlCommand("insert into FTAB (CMTID, IMAGEDT, COMMENT, DATETM, HEADING) values (#imgid, #image, #comment, #datetm, #heading)", conn);
SqlParameter imgid = new SqlParameter("#imgid",SqlDbType.Int);
imgid.Value = MaxID;
cmd.Parameters.Add(imgid);
SqlParameter uploading = new SqlParameter("#image", SqlDbType.Image);
uploading.Value = img;
cmd.Parameters.Add(uploading);
SqlParameter cmtt = new SqlParameter("#comment", SqlDbType.NVarChar);
cmtt.Value = RadTextBox3.Text;
cmd.Parameters.Add(cmtt);
SqlParameter dttm = new SqlParameter("#datetm", SqlDbType.DateTime);
dttm.Value = DateTime.Now;
cmd.Parameters.Add(dttm);
SqlParameter hhding = new SqlParameter("#heading", SqlDbType.NVarChar);
hhding.Value = RadTextBox8.Text;
cmd.Parameters.Add(hhding);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
lblHead.Text = "Image uploaded";
}
else
{
lblHead.Text = "No file selected";
}
Try this
SqlCommand cmd = new SqlCommand("select IMAGEDT from FTAB", new SqlConnection("your connection string"));
object data = cmd.ExecuteScalar();
byte[] imgBytes = (byte[])data;
System.Web.UI.WebControls.Image img = new System.Web.UI.WebControls.Image();
string filePath = Server.MapPath("temp") + "//" + "img"+DateTime.Now.Ticks.ToString()+".png";
FileStream fs = File.Create(filePath);
fs.Write(imgBytes, 0, imgBytes.Length);
fs.Flush();
fs.Close();
img.ImageUrl = filePath;
But I would say this isn't the best way to do it, you should save your uploaded images as files as in your website and save the path of that file in your database.
yogi's answer is correct in principle, but requires a web server directory "temp" and write access on an IIS directory.
You can also solve the problem using an image handler as explained in another SO question. Database retrieval is done in the same way, but the rendering happens from a stream, rather than a temp file.

Categories