I have a table with varbinary column:
My table {
Id int primary key,
FileBody varbinary(max)
}
Now I want to read it. It's simple I can use code like in this answer but it converts varbinary to byte[] and I want to avoid it.
Is it possible to put varbinary in MemoryStream without converting to a byte[]?
You can user a SqlDataReader.GetBytes to read data from the column or use SqlDataReader.GetSqlBytes or SqlDataReader.GetStream
Example :
Using getBytes
SqlCommand command = new SqlCommand( .... );
...
SqlDataReader sqlReader = command.ExecuteReader(CommandBehavior.SequentialAccess);
byte[] outbyte = new byte[bufferSize];
sqlReader.GetBytes(1, startIndex, outbyte, 0, bufferSize);
Using GetSqlBytes
SqlCommand command = new SqlCommand( .... );
...
SqlDataReader sqlReader = command.ExecuteReader(CommandBehavior.SequentialAccess);
byte[] outbyte = sqlReader.GetSqlBytes(1).Buffer;
I believe You are searching for this:
SqlCommand command = new SqlCommand();
SqlDataReader reader = command.ExecuteReader();
System.IO.Stream stream = reader.GetStream(1); // 1 is the index of the column
GetStream method:
Retrieves binary, image, varbinary, UDT, and variant data types as a Stream.
Related
I have a Table Blob, which has a varbinary(max) as a column. Now I want to store data into the database using a Filestream. The data can be really big (in my case 1.5GB) so I dont want to load the whole data into a buffer.
What I tried:
using (FileStream fs = File.Open(#"BigData.iso", FileMode.Open))
{
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = #"...";
conn.Open();
SqlCommand command = new SqlCommand("INSERT INTO Blob Values (#0, #1)", conn);
command.Parameters.Add(new SqlParameter("0", Guid.NewGuid()));
var sqlb = new SqlBytes(fs);
command.Parameters.Add(new SqlParameter("1", SqlDbType.VarBinary, -1)).Value = sqlb;
command.ExecuteNonQuery();
}
}
But I got an OutOfMemoryException, because SqlBytes initialze its buffer to the whole size of the data.
I know there is a FILESTREAM feature from Microsoft, but I don't want to use it.
Is there a way to achieve this?
You can read the file in small chunks and append them to the data column.
You will need an IDENTITY column or another column(s) that can be used as a key to execute UPDATE statements. Here is an example using an IDENTITY column:
Create a table to store the data
CREATE TABLE [dbo].[table1](
[ID] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[Data] [varbinary](max) NULL,
)
Implement C# to insert/update data in chunks
private const string C_SqlConnectionString = #"Server=SERVERNAME;Database=DBNAME;Trusted_Connection=yes;";
private const int C_FileChunkSizeBytes = 1024 * 1024; // 1 MB
private static void storeFile(string filepath)
{
using (FileStream fs = File.Open(filepath, FileMode.Open))
{
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = C_SqlConnectionString;
conn.Open();
// Use a transaction to ensure that all parts of the file get stored to DB
SqlCommand command = new SqlCommand("BEGIN TRAN", conn);
command.ExecuteNonQuery();
var pos = 0;
byte[] fileBytes = null;
int sqlRowId = 0;
// Read the file in chunks
while (pos < fs.Length)
{
// Read file bytes
var bytesToRead = pos + C_FileChunkSizeBytes < fs.Length
? C_FileChunkSizeBytes
: (int)(fs.Length - pos);
fileBytes = new byte[bytesToRead];
fs.Read(fileBytes, 0, bytesToRead);
// Store bytes to a parameter
var varbinary = new SqlParameter("0", System.Data.SqlDbType.VarBinary, -1);
varbinary.Value = fileBytes;
if (pos == 0)
{
// If this is the first chunk, then we need to INSERT
// The HOLDLOCK hint will hold a lock on the table until transaction completes (or is rolled back)
command = new SqlCommand("INSERT INTO [dbo].[table1] WITH(HOLDLOCK) VALUES(#0)", conn);
command.Parameters.Add(varbinary);
command.ExecuteNonQuery();
// Get the row ID for the inserted row
command = new SqlCommand("SELECT ##IDENTITY", conn);
sqlRowId = Convert.ToInt32(command.ExecuteScalar());
}
else
{
// Update existing row and append data
command = new SqlCommand("UPDATE [dbo].[table1] SET [Data] = [Data] + #0 WHERE [ID] = #1", conn);
command.Parameters.Add(varbinary);
command.Parameters.Add(new SqlParameter("1", System.Data.SqlDbType.Int)).Value = sqlRowId;
command.ExecuteNonQuery();
}
// ** Good place for a breakpoint
pos += bytesToRead;
}
// Commit transaction
command = new SqlCommand("COMMIT TRAN", conn);
command.ExecuteNonQuery();
conn.Close();
}
}
}
Testing
Place a breakpoint in C# code at the bottom of the while loop, eg at pos += bytesToRead;.
Whilst executing the code, when code execution stops at the breakpoint, check the data in SQL:
SELECT *
,LEN([Data]) AS [Length]
FROM [dbo].[table1] WITH(NOLOCK)
The NOLOCK hint will let us see data in uncommitted transactions. LEN([Data]) will show how field length grows after each iteration of the while loop.
I have a table that have a field as Varbinary(MAX) datatype and I want to insert image file from PictureBox into this column. So how can I convert and insert image to Varbinary in Sql Server. Thank for help me.
You should really show some code, what have you tried...
This is only a guess, but it should give you a clue how to insert picture into database:
//byte array that will hold image data
byte[] imageData = null;
using (var ms = new MemoryStream())
{
//here is image property of your pictureBox control saved into memory stream
pictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
imageData = ms.ToArray();
}
//make sql connection
SqlConnection conn = new SqlConnection("your connection string goes here");
// command with parameter
SqlCommand cmd = new SqlCommand("insert into TableWithImages (imageData) values (#imageData);", conn);
//define param and pass byte array as value
cmd.Parameters.Add("#imageData", SqlDbType.VarBinary).Value = imageData;
//do insert
cmd.ExecuteNonQuery();
I don't know what's wrong with my code in retrieving image in db. I insert image without using image path file cause the image provide by the cam.
Here's my code in inserting image in db
Image img = pictureBox1.Image;
MemoryStream memStream = new MemoryStream();
img.Save(memStream, ImageFormat.Bmp);
byte[] imageBt = memStream.ToArray();
query = "";
query += "INSERT INTO table(picture) VALUES ('" + imageBt + "')";
cmd = new MySqlCommand(query, con);
cmd.ExecuteNonQuery();
con.close();
Here's my code in retrieving image in db
query = "";
query += "SELECT picture FROM table WHERE id = '1'";
cmd = new MySqlCommand(query, con);
con.Open();
MemoryStream ms = new MemoryStream();
byte[] image = (byte[])cmd.ExecuteScalar();
ms.Write(image, 0, image.Length);
con.Close();
Bitmap bmp = new Bitmap(ms)
pictureBox1.Image = bmp; //Still get the Error here parameter is not valid
Is there anywrong process in saving image in database. Btw my image type in db is Blob. I don't know why it doesn't work in retrieving image it always thrown error. Thanks
When reading, you haven't rewound the stream. If you Write, you must set ms.Position = 0; afterwards. Or simpler: create the stream from the data, then you don't need to:
byte[] image = ...
var ms = new MemoryStream(image);
When writing, you seem to have injected the data directly. That... almost certainly won't work - in fact, you're probably writing System.Byte[] to the command. Ideally, use a parameter:
query = "INSERT INTO table(picture) VALUES (#blob)";
cmd = new MySqlCommand(query, con);
cmd.Parameters.AddWithValue("blob", imageBt);
cmd.ExecuteNonQuery();
(the exact syntax may change between RDBMS implementations)
I'm developing an app in C# with .NET and MySQL database. I need to be able to insert and retrieve images in and out of the database and I have a column named 'Image' of type LONGBLOB for that purpose. The insertion goes well but when I try to retrieve the blob the following error pops up:
GetBytes() can only be called on binary or GUID columns
I'm using the following code to select from the database:
Conn.Open();
string sql = #"SELECT `ID`, `Image`, `Note`"
+ " FROM `Item`"
+ " WHERE `ID` = ?ID";
MySqlCommand cmd = new MySqlCommand(sql, Conn);
cmd.Parameters.Add(new MySqlParameter("?ID", iD));
cmd.Prepare();
rdr = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
while (rdr.Read())
{
this.ID= rdr.GetString("ID");
if (!rdr.IsDBNull(1))
{
long len = rdr.GetBytes(1, 0, null, 0, 0);
byte[] ImageBytes = new byte[len];
rdr.GetBytes(1, 0, ImageBytes, 0, (int)len);
MemoryStream ms = new MemoryStream(ImageBytes);
this.Image = Image.FromStream(ms);
}
this.Note = rdr.GetString("Note");
Despite changing the column type into binary and varbinary, I still got the same error.
Does anyone know what I'm doing wrong here?
TIA
Can't you just cast rdr["Image"] as byte[]?
I have to persist a .csv in my database, but for a more testable application I prefer don't use procedures.
Basically I just generate a file and the next instruction is put this in database.
Someone have some clue about best way to do this in code?
Here is an example to insert blob data in oracle using c# and procedures (you said prefer that means you may).
using System;
using System.Data;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using System.IO;
using System.Text;
//Step 1
// Connect to database
// Note: Modify User Id, Password, Data Source as per your database setup
string constr = "User Id=Scott;Password=tiger;Data Source=orcl9i";
OracleConnection con = new OracleConnection(constr);
con.Open();
Console.WriteLine("Connected to database!");
// Step 2
// Note: Modify the Source and Destination location
// of the image as per your machine settings
String SourceLoc = "D:/Images/photo.jpg";
String DestinationLoc = "D:/Images/TestImage.jpg";
// provide read access to the file
FileStream fs = new FileStream(SourceLoc, FileMode.Open,FileAccess.Read);
// Create a byte array of file stream length
byte[] ImageData = new byte[fs.Length];
//Read block of bytes from stream into the byte array
fs.Read(ImageData,0,System.Convert.ToInt32(fs.Length));
//Close the File Stream
fs.Close();
// Step 3
// Create Anonymous PL/SQL block string
String block = " BEGIN " +
" INSERT INTO testblob (id, photo) VALUES (100, :1); " +
" SELECT photo into :2 from testblob WHERE id = 100; " +
" END; ";
// Set command to create Anonymous PL/SQL Block
OracleCommand cmd = new OracleCommand();
cmd.CommandText = block;
cmd.Connection = con;
// Since executing an anonymous PL/SQL block, setting the command type
// as Text instead of StoredProcedure
cmd.CommandType = CommandType.Text;
// Step 4
// Setting Oracle parameters
// Bind the parameter as OracleDbType.Blob to command for inserting image
OracleParameter param = cmd.Parameters.Add("blobtodb", OracleDbType.Blob);
param.Direction = ParameterDirection.Input;
// Assign Byte Array to Oracle Parameter
param.Value = ImageData;
// Bind the parameter as OracleDbType.Blob to command for retrieving the image
OracleParameter param2 = cmd.Parameters.Add("blobfromdb", OracleDbType.Blob);
param2.Direction = ParameterDirection.Output;
// Step 5
// Execute the Anonymous PL/SQL Block
// The anonymous PL/SQL block inserts the image to the
// database and then retrieves the images as an output parameter
cmd.ExecuteNonQuery();
Console.WriteLine("Image file inserted to database from " + SourceLoc);
// Step 6
// Save the retrieved image to the DestinationLoc in the file system
// Create a byte array
byte[] byteData = new byte[0];
// fetch the value of Oracle parameter into the byte array
byteData = (byte[])((OracleBlob)(cmd.Parameters[1].Value)).Value;
// get the length of the byte array
int ArraySize = new int();
ArraySize = byteData.GetUpperBound(0);
// Write the Blob data fetched from database to the filesystem at the
// destination location
FileStream fs1 = new FileStream(#DestinationLoc,
FileMode.OpenOrCreate, FileAccess.Write);
fs1.Write(byteData, 0,ArraySize);
fs1.Close();
Console.WriteLine("Image saved to " + DestinationLoc + " successfully !");
Console.WriteLine("");
Console.WriteLine("***********************************************************");
Console.WriteLine("Before running this application again, execute 'Listing 1' ");
private void btnSave_Click(object sender, EventArgs e)
{
try
{
//Read Image Bytes into a byte array
byte[] blob = ReadFile(txtPath.Text);
//Initialize Oracle Server Connection
con = new OracleConnection(conString);
//Set insert query
string qry = "insert into Imgpn (imgpath,photo) values('" + txtPath.Text + "'," + " :BlobParameter )";
OracleParameter blobParameter = new OracleParameter();
blobParameter.OracleType = OracleType.Blob;
blobParameter.ParameterName = "BlobParameter";
blobParameter.Value = blob;
//Initialize OracleCommand object for insert.
cmd = new OracleCommand(qry, con);
//We are passing Name and Blob byte data as Oracle parameters.
cmd.Parameters.Add(blobParameter);
//Open connection and execute insert query.
con.Open();
cmd.ExecuteNonQuery();
MessageBox.Show("Image added to blob field");
GetImagesFromDatabase();
cmd.Dispose();
con.Close();
//this.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
byte[] ReadFile(string sPath)
{
//Initialize byte array with a null value initially.
byte[] data = null;
//Use FileInfo object to get file size.
FileInfo fInfo = new FileInfo(sPath);
long numBytes = fInfo.Length;
//Open FileStream to read file
FileStream fStream = new FileStream(sPath, FileMode.Open, FileAccess.Read);
//Use BinaryReader to read file stream into byte array.
BinaryReader br = new BinaryReader(fStream);
//When you use BinaryReader, you need to supply number of bytes to read from file.
//In this case we want to read entire file. So supplying total number of bytes.
data = br.ReadBytes((int)numBytes);
return data;
}
void GetImagesFromDatabase()
{
try
{
//Initialize Oracle connection.
con = new OracleConnection(conString);
//MessageBox.Show("Connection Successfull");
//Initialize Oracle adapter.
OracleDataAdapter oda = new OracleDataAdapter("Select * from Imgpn", con);
//Initialize Dataset.
DataSet DS = new DataSet();
//Fill dataset with ImagesStore table.
oda.Fill(DS, "Imgpn");
//Fill Grid with dataset.
dataGridView1.DataSource = DS.Tables["Imgpn"];
//
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
here is the simple way to insert image into oracle database ane retrieve ane show in datagridview