I have a database (SQL 2008) where I store file's in.
These are saved as a varbinary(max) type.
Now I need to get the .txt file again so I can loop through the contents of the file like i used to do with StreamReader.
while ((line = file.ReadLine()) != null)
{
string code = line.Substring(line.Length - 12);
}
But how can I convert the varbinary byte[] back to the normal .txt file so I'm able to go through the contents line by line.
I found some ideas with memorystream or filestream but can't get them to work.
Thanks in advance!
MemoryStream m = new MemoryStream(byteArrayFromDB);
StreamReader file = new StreamReader(m);
while ((line = file.ReadLine()) != null)
{
string code = line.Substring(line.Length - 12);
}
try this:
System.IO.File.WriteAllBytes("path to save your file", bytes);
cv is a varbinary(max) field
SqlCommand sqlCmd = new SqlCommand("SELECT cv FROM [job].[UserInfo] Where ID = 39 ", conn);
SqlDataReader reader = sqlCmd.ExecuteReader();
if (reader.Read() != null)
{
byte[] buffer = (byte[])reader["cv"];
File.WriteAllBytes("c:\\cv1.txt", buffer);
}
static void Main(string[] args)
{
GetData();
}
public static void GetData()
{
SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["main"].ConnectionString);
conn.Open();
string query = "SELECT FileStream FROM [EventOne] Where ID=2";
SqlCommand cmd = new SqlCommand(query, conn);
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
byte[] buffer = dt.AsEnumerable().Select(c => c.Field<byte[]>("FileStream")).SingleOrDefault();
File.WriteAllBytes("c:\\FileStream.txt", buffer);
}
Related
I want to add files to the database.
I have 6 columns to store files. If only 1 file is uploaded, I want to upload the single file in only one column. If two files are uploaded I want to use 2 columns.
What should my SQL query be to update accordingly when the files are uploaded?
I have restricted the number of files uploaded to 6 and their total size to 2 MB.
I'm using ASP.NET.
protected void btnUpload_Click(object sender, EventArgs e)
{
try
{
if (fileUpload.PostedFiles.Count <= 6)
{
int a = fileUpload.PostedFile.ContentLength;
if (a < 2000)
{
foreach (HttpPostedFile postedFile in fileUpload.PostedFiles)
{
string filename = Path.GetFileName(postedFile.FileName);
string contentType = postedFile.ContentType;
using (Stream fs = postedFile.InputStream)
{
using (BinaryReader br = new BinaryReader(fs))
{
byte[] bytes = br.ReadBytes((Int32)fs.Length);
using (SqlConnection con = new SqlConnection(#"Data Source= USER\SQLEXPRESS ; Initial Catalog= BlobUploading ; Integrated Security = True"))
{
string query = "insert into tblBlob values (#BloB1, #BloB2, #BloB3,#BloB4 ,#BloB5 ,#BloB6)";
using (SqlCommand cmd = new SqlCommand(query))
{
cmd.Connection = con;
cmd.Parameters.AddWithValue("#BloB1", bytes[0]);
cmd.Parameters.AddWithValue("#BloB2", bytes[1]);
cmd.Parameters.AddWithValue("#BloB3", bytes[2]);
cmd.Parameters.AddWithValue("#BloB4", bytes[3]);
cmd.Parameters.AddWithValue("#BloB5", bytes[4]);
cmd.Parameters.AddWithValue("#BloB6", bytes[5]);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
}
}
}
}
This is my update code, i know that inserting the values of bytes into the table is wrong!
Wrap your insert query into a Stored Procedure. In Stored Procedure, set the default value of Blob to Null.
From ASP.NET, set the blob parameters according to the availability of file. If you do not set a blob parameter, then it will be set to null in SP.
string strQuery = "sp_SaveFile";
SqlCommand cmd = new SqlCommand(strQuery);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#FormId", SqlDbType.Int).Value = fileId;
if(bytes1 != null)
cmd.Parameters.Add("#Blob1", SqlDbType.Binary).Value = bytes1;
if(bytes2 != null)
cmd.Parameters.Add("#Blob2", SqlDbType.Binary).Value = bytes2;
// .... Handle rest of the Blob parameters.
InsertUpdateData(cmd);
You need to redesign your database structure so that there is only one blob field, a record ID field as PK and a load identifier to identify which file if there are multiple files. Your insert query might look like:
INSERT INTO myTable
(
fileID,
fileContents
)
VALUES
(1, fileContent1),
(2 ,fileContent2)
You can then vary the number of added records depending on how many are needed.
Insert multiple rows, not multiple columns as #juharr says.
To do this in ASP.NET, C# you need structure to hold your files:
public class FileHolder
{
int FileID {get; set; }
byte[] FileData {get; set;}
}
Create a List<FileHolder> files to hold the data. Then:
foreach (var item in files)
{
// write item.FileID and item.FileData
// into your table
}
You are then not restricted to the number of files you can process at any one time.
protected void btnUpload_Click(object sender, EventArgs e)
{
try
{
if (fileUpload.PostedFiles.Count <= 6)
{
int a = fileUpload.PostedFile.ContentLength;
if (a < 2000)
{
foreach (HttpPostedFile postedFile in fileUpload.PostedFiles)
{
string filename = Path.GetFileName(postedFile.FileName);
string contentType = postedFile.ContentType;
using (Stream fs = postedFile.InputStream)
{
using (BinaryReader br = new BinaryReader(fs))
{
byte[] bytes = br.ReadBytes((Int32)fs.Length);
using (SqlConnection con = new SqlConnection(#"Data Source= USER\SQLEXPRESS ; Initial Catalog= BlobUploading ; Integrated Security = True"))
{
string query = "insert into tblBlob values (#BloB1, #BloB2, #BloB3,#BloB4 ,#BloB5 ,#BloB6)";
using (SqlCommand cmd = new SqlCommand(query))
{
cmd.Connection = con;
cmd.Parameters.AddWithValue("#BloB1", bytes[0]);
cmd.Parameters.AddWithValue("#BloB2", bytes[1]);
cmd.Parameters.AddWithValue("#BloB3", bytes[2]);
cmd.Parameters.AddWithValue("#BloB4", bytes[3]);
cmd.Parameters.AddWithValue("#BloB5", bytes[4]);
cmd.Parameters.AddWithValue("#BloB6", bytes[5]);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
}
}
}
}
This is my update code, i know that inserting the values of bytes into the table is wrong!
I have the following scenario:
I have successfully saved my files(various extensions) into my sql server db using FILESTREAM. These can be anything from an image, to a word doc, pdf etc.
now i want to retreive them and save them as a file to my local directory.
here is what i have so far
My function calling SQL and getting the the filestream information i want
public static void SelectFile(string sourceId)
{
string serverPath;
string filename;
byte[] serverTxn;
using (TransactionScope ts = new TransactionScope())
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.AppSettings["DBConn"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("OPS.LoadFileBlobFromSQL", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#AttachmentId", SqlDbType.VarChar).Value = sourceId;
using (SqlDataReader rdr = cmd.ExecuteReader())
{
rdr.Read();
filename = rdr.GetSqlString(0).Value;
serverPath = rdr.GetSqlString(1).Value;
serverTxn = rdr.GetSqlBinary(2).Value;
rdr.Close();
}
}
StreamObjectFromFilestream(serverPath, serverTxn, filename);
}
ts.Complete();
}
}
private static void StreamObjectFromFilestream(string serverPath, byte[] serverTxn, string filename)
{
SqlFileStream sfs = new SqlFileStream(serverPath, serverTxn, FileAccess.Read);
byte[] buffer = new byte[sfs.Length];
sfs.Read(buffer, 0, buffer.Length);
System.IO.File.WriteAllBytes(#"c:\test\hello.pdf", buffer);
sfs.Close();
}
I am getting serverpath, filename and serverTxn .. but when i go into my StreamObjectFromFilestream function the buffer is empty.. i know i am missing something simple here ... just dont know what.
Any pointers in the right direction would be appreciated.
Thanks,
Corey
You can skip using the SqlFilestream inside StreamObjectFromFilestram, your serverTxn is already a byte array that the Method WriteAllBytes is needing.
I've got to read and put text into a SQL Server table.
I initially started with a BULK INSERT, but I have the problem that I don't now the encoding of the files and so the BULK sometimes outputs some errors.
Then, I choose to make through a C# Console Aplication, and everything works fine, except when I have large files. To about 300-400 MB, I don't have any problem, but above that, I'm getting a OOM error.
Here's my code:
static void Main()
{
string fullFileName = #"C:\Temp\410.4604";
string FileName = System.IO.Path.GetFileName(fullFileName);
string table = FileName.Substring(0, 3);
Console.WriteLine("Iniciou às: " + inicio);
DataTable data = new DataTable();
data.Clear();
data.Columns.Add("Text");
using (System.IO.FileStream fs = System.IO.File.Open(fullFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read))
using (System.IO.BufferedStream bs = new System.IO.BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
string line;
while ((line = sr.ReadLine()) != null)
{
data.Rows.Add(line);
}
}
var conn = new System.Data.SqlClient.SqlConnection(myConn);
SqlCommand insertCommand = new SqlCommand(myProc, conn);
insertCommand.CommandTimeout = 0;
insertCommand.CommandType = CommandType.StoredProcedure;
SqlParameter insParam1 = insertCommand.Parameters.AddWithValue("#data", data);
insParam1.SqlDbType = SqlDbType.Structured;
SqlParameter insParam2 = insertCommand.Parameters.AddWithValue("#table", table);
conn.Open();
insertCommand.ExecuteNonQuery();
conn.Close();
data.Clear();
data = null;
GC.Collect();
Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
Any ideas? Or advices to improve this?
Many thanks
I have seen many solutions to this problem where people will just use Command.ExecuteScalar as byte[]; but their SQL queries are getting one varbinary field at a time. I am trying to select about 30k rows of varbinary entries, but them in a byte[] and deserialize.
Here is my code:
public void MNAdapter()
{
IsoStorage retVal = new IsoStorage();
SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder();
csb.DataSource = #"LocalMachine\SQLDEV";
csb.InitialCatalog = "Support";
csb.IntegratedSecurity = true;
string connString = csb.ToString();
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand command = conn.CreateCommand();
command.CommandText = #"SELECT S.Settings
from Support.dbo.SavedLocalSettings S
inner join WebCatalog.Published.People P
on P.PKey = S.PeopleLink
inner join WebCatalog.Published.Company C
on P.Link = C.PeopleList
where S.DateSaved >= GETDATE()-34
and C.PKey != '530F4622-C30D-DD11-A23A-00304834A8C9'
and C.PKey != '7BAF7229-9249-449E-BEA5-4B366D7ECCD1'
and C.PKey != 'CCBB2140-C30D-DD11-A23A-00304834A8C9'
and S.CompanyName not like 'Tech Support%'
Group By S.PeopleLink, S.Settings";
using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
{
//DataTable dt = new DataTable();
//dt.Load(reader);
byte[] blob = null;
BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new CustomBinder();
while (reader.Read())
{
reader.GetBytes(0,0,blob,0,100000);
Console.WriteLine(blob.ToString());
retVal = bf.Deserialize(new MemoryStream(blob)) as IsoStorage;
}
}
}
}
I also tried putting them in a Data tabla first even though I thought that would be redundant, but they get read in as integers.
I don't get any errors and the data is going into the data reader, but it's like reader.GetBytes(0,0,blob,0,100000); is not even running because blob stays null.
Why not use:
blob = (byte[])reader.Items["Settings"];
Or
blob = (byte[])reader["Settings"];
reader.GetBytes(0,0,blob,0,100000);
You expect this method to create the array of bytes for you. It won't - it needs a reference to an existing array. You have to prepare the array yourself.
i'm trying to load images from database to a PictureBox. I use these following codes in order to load them to my picture. I've written some code but don't know what I should do for continuing.
Any help will be appreciated.
private void button1_Click(object sender, EventArgs e)
{
sql = new SqlConnection(#"Data Source=PC-PC\PC;Initial Catalog=Test;Integrated Security=True");
cmd = new SqlCommand();
cmd.Connection = sql;
cmd.CommandText = ("select Image from Entry where EntryID =#EntryID");
cmd.Parameters.AddWithValue("#EntryID", Convert.ToInt32(textBox1.Text));
}
Continue with something like this in the button1_Click:
// Your code first, here.
var da = new SqlDataAdapter(cmd);
var ds = new DataSet();
da.Fill(ds, "Images");
int count = ds.Tables["Images"].Rows.Count;
if (count > 0)
{
var data = (Byte[])ds.Tables["Images"].Rows[count - 1]["Image"];
var stream = new MemoryStream(data);
pictureBox1.Image = Image.FromStream(stream);
}
Assuming we have a simple database with a table called BLOBTest:
CREATE TABLE BLOBTest
(
BLOBID INT IDENTITY NOT NULL,
BLOBData IMAGE NOT NULL
)
We could retrieve the image to code in the following way:
try
{
SqlConnection cn = new SqlConnection(strCn);
cn.Open();
//Retrieve BLOB from database into DataSet.
SqlCommand cmd = new SqlCommand("SELECT BLOBID, BLOBData FROM BLOBTest ORDER BY BLOBID", cn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds, "BLOBTest");
int c = ds.Tables["BLOBTest"].Rows.Count;
if(c>0)
{ //BLOB is read into Byte array, then used to construct MemoryStream,
//then passed to PictureBox.
Byte[] byteBLOBData = new Byte[0];
byteBLOBData = (Byte[])(ds.Tables["BLOBTest"].Rows[c - 1]["BLOBData"]);
MemoryStream stmBLOBData = new MemoryStream(byteBLOBData);
pictureBox1.Image= Image.FromStream(stmBLOBData);
}
cn.Close();
}
catch(Exception ex)
{MessageBox.Show(ex.Message);}
This code retrieves the rows from the BLOBTest table in the database into a DataSet, copies the most recently added image into a Byte array and then into a MemoryStream object, and then loads the MemoryStream into the Image property of the PictureBox control.
Full reference guide:
http://support.microsoft.com/kb/317701
private void btnShowImage_Click(object sender, EventArgs e)
{
string constr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=.\\PIS(ACU).mdb;";
Con = new OleDbConnection(#constr);
Con.Open();
Com = new OleDbCommand();
Com.Connection = Con;
Com.CommandText = "SELECT Photo FROM PatientImages WHERE Patient_Id = " + val + " ";
OleDbDataReader reader = Com.ExecuteReader();
if (reader.Read())
{
byte[] picbyte = reader["Photo"] as byte[] ?? null;
if (picbyte != null)
{
MemoryStream mstream = new MemoryStream(picbyte);
pictureBoxForImage.Image = System.Drawing.Image.FromStream(mstream);
{
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(mstream);
}
}
HERE IS A TOTAL ANOTHER WAY TO DO SO:
You can simply convert the Image to Text before saving it into DataBase and the then convert it back to the Image after reading it:
public string ImageToStringFucntion(Image img)
{
try
{
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
byte[] imgBytes = ms.ToArray();
string FinalText = Convert.ToBase64String(imgBytes, 0 , imgBytes.Length);
return FinalText;
}
}
catch
{
return null;
}
}
Now you can Insert or Update your Database...
Now Let's consider you want it back:
public Image StringToImage_(string input_)
{
try
{
byte[] imgBytes = Convert.FromBase64String(input_);
using (MemoryStream ms = new MemoryStream(imgBytes))
{
Image img = Image.FromStream(ms, true);
return img;
}
}
catch (Exception ex)
{
return null;
}
}
Now you can do as follow:
// Considering you have already pulled your data
// from database and set it in a DataSet called 'ds',
// and you picture is on the field number [1] of your DataRow
pictureBox1.Image = StringToImage_(ds.Table[0].Rows[0][1].ToString());