My question is the following: I'm trying to upload an Excel file to database with this method:
using (SqlConnection connection = new SqlConnection(#"Data Source=TESZT1\SQLEXPRESS;Initial Catalog=Alepitmeny;Persist Security Info=True;User ID=sa;Password=*****"))
using (SqlCommand command = connection.CreateCommand())
{
byte[] file;
using (var stream = new FileStream(ExcelFilePath, FileMode.Open, FileAccess.Read))
{
using (var reader = new BinaryReader(stream))
{
file = reader.ReadBytes((int)stream.Length);
}
}
command.CommandText = "INSERT INTO Dokumentacio (Elrendelo_ExcelFile) VALUES (#File) SELECT SCOPE_IDENTITY()";
command.Parameters.Add("#File", SqlDbType.VarBinary, file.Length).Value = file;
connection.Open();
this.dokumentacio_Class.Dokumentacio_ID = Convert.ToInt32(command.ExecuteScalar());
connection.Close();
}
But when I'm downloading the uploaded files with the method below, I get an error message
Excel found unreadable content in filename.xls. Do you want to recover the contents of this workbook?
from Microsoft Excel, and it can't recover it.
(I'm using SQL Server 2012, Visual Studio 2013, the project is WPF project, my Office version is 2013)
In the database, Elrendelo_ExcelFile column is VARBINARY(MAX)
public bool ElrendeloExcelFileLetolt(string SavePath)
{
using (SqlConnection connection = new SqlConnection(#"Data Source=TESZT1\SQLEXPRESS;Initial Catalog=Alepitmeny;Persist Security Info=True;User ID=sa;Password=*****"))
try
{
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = #"SELECT d.Elrendelo_ExcelFile FROM Dokumentacio d INNER JOIN Kapcsolotabla k ON k.Dokumentacio_ID=d.Dokumentacio_ID WHERE k.Elrendelo_ID=#id";
command.Parameters.AddWithValue("#id", this.dokumentacio_ID);
FileStream stream;
BinaryWriter writer;
int bufferSize = 100;
byte[] buffer = new byte[bufferSize];
long retval;
long startIndex = 0;
connection.Open();
SqlDataReader reader = command.ExecuteReader(CommandBehavior.Default);
while (reader.Read())
{
stream = new FileStream(SavePath, FileMode.OpenOrCreate, FileAccess.Write);
writer = new BinaryWriter(stream);
startIndex = 0;
retval = reader.GetBytes(0, startIndex, buffer, 0, bufferSize);
while (retval == bufferSize)
{
writer.Write(buffer);
writer.Flush();
startIndex += bufferSize;
retval = reader.GetBytes(0, startIndex, buffer, 0, bufferSize);
}
writer.Write(buffer, 0, (int)retval - 1);
writer.Flush();
writer.Close();
stream.Close();
}
reader.Close();
connection.Close();
}
return true;
}
catch (System.Data.SqlClient.SqlException)
{
return false;
}
finally
{
connection.Close();
}
}
This SO answer should help you -> How do I insert/retrieve Excel files to varbinary(max) column in SQL Server 2008?
Related
We are trying to migrate our code from ASP.NET that was written some time ago to ASP.NET Core 2.0.
This piece of code stores a document in SQL Server and retrieves it.
***Original Code:***
protected void btnUpload_Click(object sender, EventArgs e)
{
foreach (HttpPostedFile postedFile in multipleUpload.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);
string constr = ConfigurationManager.ConnectionStrings["ab"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
string query = "insert into ftr_UploadMultiple (name,contentType,data) values (#Name, #ContentType, #Data)";
using (SqlCommand cmd = new SqlCommand(query))
{
cmd.Connection = con;
cmd.Parameters.AddWithValue("#Name", filename);
cmd.Parameters.AddWithValue("#ContentType", contentType);
cmd.Parameters.AddWithValue("#Data", bytes);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
We did try with the following code, it only stores 0 bytes in the DB:
Any suggestions around this should be really helpful.
Our Code in ASP.NET Core 2.0
if (file.Length > 0)
{
using (var stream = new
FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
using (BinaryReader br = new BinaryReader(stream))
{
byte[] bytes = br.ReadBytes((Int32)stream.Length);
string constr = "<Connection String";
using (SqlConnection con = new SqlConnection(constr))
{
string query = "insert into ftr_UploadMultiple (data) values (#Data)";
using (SqlCommand cmd = new SqlCommand(query))
{
cmd.Connection = con;
cmd.Parameters.AddWithValue("#Data", bytes);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
I have deliberately removed the closing }s. Also, facing an issue in Downloading already uploaded file as Response.Binarywrite() is not available in ASP.NET Core 2.0.
After you call CopyToAsync to copy the bytes from the upload file to the filestream, the filestream's position is at the end. When you then attempt to read from the filestream, you're only reading the null byte at the end, resulting in 0 bytes being read.
The simplest solution is to just add the following before you read:
stream.Position = 0;
However, unless you actually need to write the file to the filesystem as well, this is just extraneous work. It would be better to copy the upload file's stream to a MemoryStream and then simply use ToArray to get the bytes from that: no need for additional reader.
Try get bytes array from InputStream
// Read bytes from http input stream
BinaryReader b = new BinaryReader(file.InputStream);
byte[] binData = b.ReadBytes(file.ContentLength);
ASP.NET Core
if (file.Length > 0)
{
using (BinaryReader br = new BinaryReader(file.InputStream))
{
/* ... use file.Length or file.ContentLength */
byte[] bytes = br.ReadBytes(file.Length);
/* ... File Processing (bytes) */
}
}
.
This question already has an answer here:
No imaging component suitable to complete the operation was found WPF vb.net
(1 answer)
Closed 4 years ago.
I'm trying to load image from my .mdb database in WPF. I'm using this code :
public void loadimg()
{
con.Open();
OleDbCommand cmd = new OleDbCommand("Select * from recents", con);
DataTable table = new DataTable;
OleDbDataAdapter adap = new OleDbDataAdapter(cmd);
adap.Fill(table);
if (table.Rows.Count <= 0)
{
MsgBox("nooo");
}
else
{
MemoryStream stream = new MemoryStream();
StreamWriter stm;
BinaryWriter writer = new BinaryWriter(stream);
int bufferSize = 100;
byte[] outByte = new byte[bufferSize + 1];
long retval;
long startIndex = 0;
string pubID = "";
OleDbDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
reader.Read();
while (reader.Read())
{
startIndex = 0;
retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize);
while (retval == bufferSize)
{
writer.Write(outByte);
writer.Flush();
startIndex += bufferSize;
retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize);
}
writer.Write(outByte, 0, (int)retval - 1);
writer.Flush();
}
reader.Close();
con.Close();
stream.Position = 0;
stream.Seek(0, SeekOrigin.Begin);
System.Drawing.Image _Image = System.Drawing.Image.FromStream(stream);
image1.Source = System.Windows.Media.Imaging.BitmapFrame.Create(stream);
}
}
The code above returns an error :
No imaging component suitable to complete this operation was found.
I spent hours trying to figure out how to fix it.Any help would be highly appreciated.
Update
In the comments, i was asked if i inserted the data properly..Well,here's the code i used to insert the data :
public void adddata()
{
con.Open();
OleDbCommand cmd = new OleDbCommand("Insert into recents(Pic)values(#pic)", con);
byte[] data;
System.Drawing.Image myimage = System.Drawing.Image.FromFile("E:\\19686468_1419770068104721_1127495277_o.png");
using (MemoryStream ms = new MemoryStream())
{
myimage.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
data = ms.ToArray();
}
cmd.Parameters.AddWithValue("#pic", data);
cmd.ExecuteNonQuery();
con.Close();
}
Please help me out!
Fixed it...For anyone who faces this error in future :
Make sure you're inserting the data in the proper way(sometimes corrupted data in the db causes such errors)
2 . You don't need to do some heavy coding to convert the image to byte!
Finally,let's code :
public void loadimg()
{
con.Open();
OleDbCommand cmd = new OleDbCommand("Select * from recents", con);
OleDbDataReader _dr;
_dr = cmd.ExecuteReader;
byte[] _photo;
while (_dr.Read())
{
try
{
_photo = (byte[])_dr(1);
BitmapImage bi = new BitmapImage();
using (MemoryStream strm = new MemoryStream(_photo))
{
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.StreamSource = strm;
bi.EndInit();
}
image1.Source = bi;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
I am saving files to a SQL server 2008 (Express) database using FILESTREAM, the trouble I'm having is that certain files seem to be getting corrupted in the process.
For example if I save a word or excel document in one of the newer formats (docx, or xslx) then when I try to open the file I get an error message saying that the data is corrupted and would I like word/excel to try and recover it, If I click yes office is able to 'recover' the data and opens the file in compatibility mode.
However if i zip the file first then after extracting the contents I'm able to open the file without a problem. Strangely If I save an mp3 file to the database then I have the reverse issue, I can open the file no problem, but If I saved a zipped version of the mp3 I can't even extract the contents of that zip. When I tried to save a pdf or power-point file I ran into similar problems (the pdf i could only read if I zipped it first, and the ppt I couldn't read at all).
Update: here's my code that I'm using to write to the database and to read
To write to the database:
SQL = "SELECT Attachment.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Activity " +
"WHERE RowID = CAST(#RowID as uniqueidentifier)";
transaction = connection.BeginTransaction();
command.Transaction = transaction;
command.CommandText = SQL;
command.Parameters.Clear();
command.Parameters.Add(rowIDParam);
SqlDataReader readerFS = null;
readerFS= command.ExecuteReader();
string path = (string)readerFS[0].ToString();
byte[] context = (byte[])readerFS[1];
int length = context.Length;
SqlFileStream targetStream = new SqlFileStream(path, context, FileAccess.Write);
int blockSize = 1024 * 512; //half a megabyte
byte[] buffer = new byte[blockSize];
int bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
targetStream.Write(buffer, 0, bytesRead);
bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
}
targetStream.Close();
sourceStream.Close();
readerFS.Close();
transaction.Commit();
And to read:
SqlConnection connection = null;
SqlTransaction transaction = null;
try
{
connection = getConnection();
connection.Open();
transaction = connection.BeginTransaction();
SQL = "SELECT Attachment.PathName(), + GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Activity"
+ " WHERE ActivityID = #ActivityID";
SqlCommand command = new SqlCommand(SQL, connection);
command.Transaction = transaction;
command.Parameters.Add(new SqlParameter("ActivityID", activity.ActivityID));
SqlDataReader reader = command.ExecuteReader();
string path = (string)reader[0];
byte[] context = (byte[])reader[1];
int length = context.Length;
reader.Close();
SqlFileStream sourceStream = new SqlFileStream(path, context, FileAccess.Read);
int blockSize = 1024 * 512; //half a megabyte
byte[] buffer = new byte[blockSize];
List<byte> attachmentBytes = new List<byte>();
int bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
foreach (byte b in buffer)
{
attachmentBytes.Add(b);
}
}
FileStream outputStream = File.Create(outputPath);
foreach (byte b in attachmentBytes)
{
byte[] barr = new byte[1];
barr[0] = b;
outputStream.Write(barr, 0, 1);
}
outputStream.Close();
sourceStream.Close();
command.Transaction.Commit();
Your read code is incorrect:
while (bytesRead > 0)
{
bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
foreach (byte b in buffer)
{
attachmentBytes.Add(b);
}
}
If the bytesRead is less than buffer.Length, you still add the entire buffer to the attachementBytes. Thus, you always corrupt the document returned by adding any garbage in the end of the last buffer post bytesRead.
Other than that, allow me to have a really WTF moment. Reading a stream as a List<byte> ?? C'mon! First, I don't see the reason why you need to read into an intermediate in-memory storage to start with. You can simply read buffer by buffer and write each buffer straight into the outputStream. Second, if you must use an intermediate in-memory storage, use a MemoryStream, not a List<byte>.
I had the exact problem a few months back and figured out that I was adding an extra byte at the end of the file when reading it from FILESTREAM.
How to store the file which is uploaded by the user to the database ? I want to store the file in the database how can we do that ? In the back-end I am using sql with c#.net application.
This solution works for SQL SERVER 2005/2008.
You have to create table with VARBINARY(MAX) as one of the columns. In my case I've created Table Raporty with column RaportPlik being VARBINARY(MAX) column.
Below there are couple of support functions you can modify for your needs:
public static void databaseFilePut(string varFilePath) {
byte[] file;
using (var stream = new FileStream(varFilePath, FileMode.Open, FileAccess.Read)) {
using (var reader = new BinaryReader(stream)) {
file = reader.ReadBytes((int) stream.Length);
}
}
using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
using (var sqlWrite = new SqlCommand("INSERT INTO Raporty (RaportPlik) Values(#File)", varConnection)) {
sqlWrite.Parameters.Add("#File", SqlDbType.VarBinary, file.Length).Value = file;
sqlWrite.ExecuteNonQuery();
}
}
public static void databaseFileRead(string varID, string varPathToNewLocation) {
using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
using (var sqlQuery = new SqlCommand(#"SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = #varID", varConnection)) {
sqlQuery.Parameters.AddWithValue("#varID", varID);
using (var sqlQueryResult = sqlQuery.ExecuteReader())
if (sqlQueryResult != null) {
sqlQueryResult.Read();
var blob = new Byte[(sqlQueryResult.GetBytes(0, 0, null, 0, int.MaxValue))];
sqlQueryResult.GetBytes(0, 0, blob, 0, blob.Length);
using (var fs = new FileStream(varPathToNewLocation, FileMode.Create, FileAccess.Write)) fs.Write(blob, 0, blob.Length);
}
}
}
public static MemoryStream databaseFileRead(string varID) {
MemoryStream memoryStream = new MemoryStream();
using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
using (var sqlQuery = new SqlCommand(#"SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = #varID", varConnection)) {
sqlQuery.Parameters.AddWithValue("#varID", varID);
using (var sqlQueryResult = sqlQuery.ExecuteReader())
if (sqlQueryResult != null) {
sqlQueryResult.Read();
var blob = new Byte[(sqlQueryResult.GetBytes(0, 0, null, 0, int.MaxValue))];
sqlQueryResult.GetBytes(0, 0, blob, 0, blob.Length);
//using (var fs = new MemoryStream(memoryStream, FileMode.Create, FileAccess.Write)) {
memoryStream.Write(blob, 0, blob.Length);
//}
}
}
return memoryStream;
}
First method is to put file into database from drive, second method is to get file and save it on drive, and 3rd method is to get file from database and put it as MemoryStream so you can some other stuff with it then just writing it to drive.
This 4th method is to put MemoryStream into database:
public static int databaseFilePut(MemoryStream fileToPut) {
int varID = 0;
byte[] file = fileToPut.ToArray();
const string preparedCommand = #"
INSERT INTO [dbo].[Raporty]
([RaportPlik])
VALUES
(#File)
SELECT [RaportID] FROM [dbo].[Raporty]
WHERE [RaportID] = SCOPE_IDENTITY()
";
using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
using (var sqlWrite = new SqlCommand(preparedCommand, varConnection)) {
sqlWrite.Parameters.Add("#File", SqlDbType.VarBinary, file.Length).Value = file;
using (var sqlWriteQuery = sqlWrite.ExecuteReader())
while (sqlWriteQuery != null && sqlWriteQuery.Read()) {
varID = sqlWriteQuery["RaportID"] is int ? (int) sqlWriteQuery["RaportID"] : 0;
}
}
return varID;
}
MS SQL Server 2008 (and above, I guess) offers FileStream data type. Just Google for it (or Bing it, whatever :-)), I think you'll find what you need.
Assuming SQL 2005 or later, I would use a VarBinary(MAX) field. Pass the uploaded file as a byte[] to the insert statement. According to Microsoft, since SQL 2005, storing large chunks of data (files and images) in the DB no longer decreases performance to a great extent.
eg:
public void SaveFileToDB(string description, byte[] file)
{
using (SqlConnection con = new SqlConnection(conStr)
{
con.Open();
using (SqlCommand cmd = con.CreateCommand())
{
cmd.Parameters.Add("#Description", SqlDbType.VarChar, description);
cmd.Parameters.Add("#File", SqlDbType.VarBinary, file);
cmd.CommandText = "UploadedFileUpdate";
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
}
}
}
If you have SQL 2008, see Ron Klein's suggestion.
One solution that will keep your db size down is to store the location of the file on the server. IE a file path.
However you will have to make a manager if you ever want to move files about.
You can use a blob field type. When you read the file in from a StreamReader convert it to a byte array and then insert that into the blob field.
The reverse procedure when you want to read it, get the blob as a byte array, read it into a streamreader and write that to the response buffer.
The code previously implemented takes in the xls file saves it on to a column in a table using the stream i use the same method but the only change is the the file saved is a xlsm or an xlsx type file it saves to the column in the database
When I try and get the contents from the database and throw the saved xlsm file or xlsx file I get an error "Excel file found unreadable content do you want to recover the contents of this work book ?"
Here's the code to save the xlsm or the xlsx file
System.IO.Stream filestream = System.IO.File.Open(file, System.IO.FileMode.Open);
int fileLength = (int)filestream.Length;
byte[] input = new byte[fileLength];
filestream.Read(input, 0, fileLength);
string Sql = "insert into upload values(#contents)";
con.Open();
System.Data.SqlClient.SqlCommand c = new System.Data.SqlClient.SqlCommand(Sql, con);
c.Parameters.Add("#contents", System.Data.SqlDbType.Binary);
c.Parameters["#contents"].Value = input;
c.ExecuteNonQuery();
To retrieve and send to user
SqlCommand comm = new SqlCommand("select contents from upload order by id desc", con);
SqlDataReader reader = comm.ExecuteReader();
int bufferSize = 32768;
byte[] outbyte = new byte[bufferSize];
long retval;
long startIndex = 0;
startIndex = 0;
retval = reader.GetBytes(0, startIndex, outbyte, 0, bufferSize);
while (retval > 0)
{
System.Web.HttpContext.Current.Response.BinaryWrite(outbyte);
startIndex += bufferSize;
if (retval == bufferSize)
{
retval = reader.GetBytes(2, startIndex, outbyte, 0, bufferSize);
}
else
{
retval = 0;
}
}
A couple of things strike me as possibilities.
Firstly, you are not calling reader.Read().
Secondly, there is not need for the check on retval == bufferSize - just call GetBytes again and it will return 0 if no bytes were read from the field.
Thirdly, as you are writing to the HttpResponse you need to make sure that you call Response.Clear() before writing the bytes to the output, and Response.End() after writing the file to the response.
The other thing to try is saving the file to the hard drive and comparing it to the original. Is it the same size? If it is bigger then you are writing too much information to the file (see previous comments about HttpResponse). If it is smaller then you are not writing enough, and are most likely exiting the loop too soon (see comment about retval).
I couldn't help but notice the number of places where your code failed to wrap an IDisposable in a using block, like the following:
using (SqlConnection con = new SqlConnection(connectionString))
{
byte[] input;
using (System.IO.Stream filestream = System.IO.File.Open(file, System.IO.FileMode.Open))
{
int fileLength = (int)filestream.Length;
input = new byte[fileLength];
filestream.Read(input, 0, fileLength);
}
const string Sql = "insert into upload values(#contents)";
con.Open();
using (System.Data.SqlClient.SqlCommand c = new System.Data.SqlClient.SqlCommand(Sql, con))
{
c.Parameters.Add("#contents", System.Data.SqlDbType.Binary);
c.Parameters["#contents"].Value = input;
c.ExecuteNonQuery();
}
using (SqlCommand comm = new SqlCommand("select contents from upload order by id desc", con))
{
using (SqlDataReader reader = comm.ExecuteReader())
{
int bufferSize = 32768;
byte[] outbyte = new byte[bufferSize];
long retval;
long startIndex = 0;
startIndex = 0;
retval = reader.GetBytes(0, startIndex, outbyte, 0, bufferSize);
while (retval > 0)
{
System.Web.HttpContext.Current.Response.BinaryWrite(outbyte);
startIndex += bufferSize;
if (retval == bufferSize)
{
retval = reader.GetBytes(2, startIndex, outbyte, 0, bufferSize);
}
else
{
retval = 0;
}
}
}
}
}