Convert image into bytes to store in database - asp.net

Can anybody tell me that how to convert an image (stored in a file path) into bytes to store in a database?

Why do you want to store the path as bytes? Why don't you just store it as a string? Unless you mean you want to store the image data as bytes in the database, in which case, look into the Image data type.

To convert an image stored locally to bytes you can use IO.File.ReadAllBytes(). To convert an image on the web you can use Net.WebClient.DownloadData(). The IMAGE data type is going away in SQL Server, so use VARBINARY(MAX) to store it.
Code:
Dim url As String = "http://example.com/image.png",
file As String = "c:\image.png"
Using connection As New Data.SqlClient.SqlConnection("your connection string"),
saveImage As New Data.SqlClient.SqlCommand("dbo.saveImage", connection)
connection.Open()
saveImage.CommandType = Data.CommandType.StoredProcedure
'use only one of these
saveImage.Parameters.AddWithValue("#imageData", New Net.WebClient().DownloadData(url)) 'get file from url
'saveImage.Parameters.AddWithValue("#imageData", IO.File.ReadAllBytes(file)) 'get file locally
saveImage.ExecuteNonQuery()
End Using
Procedure:
CREATE PROCEDURE [dbo].[saveImage] ( #imageData AS VARBINARY(MAX) )
AS
INSERT INTO dbo.myImage ( imageData ) VALUES ( #imageData )
Table:
CREATE TABLE [dbo].[myImage](
[imageData] [varbinary](max) NOT NULL
)

Related

Difference between two columns with date and time imported from a CSV file

I have to import a CSV file into a SQL Server table.
The CSV has 8 columns and 2 of them are date and time values.
In my SQL table I have a column which store the difference between 2 the columns of CSV file.
I am unable to check if the cell is empty or not.
My code follows:
protected void btnImport_Click(object sender, EventArgs e)
{
string csvPath = Server.MapPath("~/Files/") + Path.GetFileName(FileUpload1.PostedFile.FileName);
FileUpload1.SaveAs(csvPath);
DataTable dt = new DataTable();
dt.Columns.AddRange(new DataColumn[8] { new DataColumn("basid", typeof(string)),
new DataColumn("EmployeeName", typeof(string)),
new DataColumn("Designation",typeof(string)),
new DataColumn("OfficeLocation",typeof(string)),
new DataColumn("Division",typeof(string)),
new DataColumn("Intime",typeof(DateTime)),
new DataColumn("Outtime",typeof(DateTime)),
new DataColumn("timedifference",typeof(DateTime))
});
string csvData = File.ReadAllText(csvPath);
foreach (string row in csvData.Split('\n'))
{
if (!string.IsNullOrEmpty(row))
{
dt.Rows.Add();
int i = 0;
foreach (string cell in row.Split(','))
{
if (i == 7)
{
dt.Rows[dt.Rows.Count - 1][i] = ((DateTime)(dt.Rows[dt.Rows.Count - 1][i - 1]) - (DateTime)(dt.Rows[dt.Rows.Count - 1][i - 2]));
}
else {
dt.Rows[dt.Rows.Count - 1][i] = cell;
}
i++;
}
}
}
string consString = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
using (SqlConnection con = new SqlConnection(consString))
{
using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(con))
{
//Set the database table name.
sqlBulkCopy.DestinationTableName = "dbo.basids";
con.Open();
sqlBulkCopy.WriteToServer(dt);
con.Close();
}
}
Understanding
Importing from a CSV file (or any other file that does not conform to the Relational Model), is not a problem. You need to understand that the file is not, and never will be, a Relational table. At best it is a file with good field types, at worst one with hopeless field types.
You may create a raw table in the database, for convenience, that is, in order to use SQL on it
At best, the fields in the file have reliable datatypes, thus the raw table may have SQL Datatypes.
At worst, the Datatypes in the raw table are all CHAR(x).
In any case, the columns in the raw table must match the import file exactly.
Usually, such raw tables do not have an Index, they are just Heaps.
Your app code will not query the raw tables, they are manipulated on by the Import code, only.
DataTable dt is such a raw table.
The Relational table (or tables) that are in the Relational database are quite a different animal. They are properly designed, Normalised, and compliant with the Relational Model. Relational tables have Primary Keys that are "made up from the data", and may have additional Unique Keys. The datatypes are "tight", and disallow illegal values.
Your app code will query the Relational tables, only.
Hence it is quite normal, that one CSV file (or import file or raw table) translates to more than one Relational table.
Therefore Import is always a two-step process:
First, just import the CSV file into the raw table, which has been suitably set up for that act.
You have not asked the question, but if I were loading this CSV file into a raw table, I wouldn't bother with all that middle-tier code, I would use the MS SQL bcp utility.
Second, INSERT-SELECT from the raw table into the Relational table(s).
In this step, the SELECT will transform the raw fields into proper Datatypes, and perform any checking that is required.
In order to ensure that the FK Constraints do not fail, the Reference tables must be populated before the data tables. For populating the Reference tables (not the raw file), eg. Operator, the code may be:
INSERT Operator
SELECT ...
FROM RawTable.Operator
WHERE RawTable.Operator NOT IN (
SELECT Operator
FROM Operator
)
ETL stands for Extract; Transform; Load. It is never Load only.
Problem
You have a raw table, only. And you think it is a Relational table. It is not.
You have no Relational tables.
Third, ...
I am unable to check if the cell is empty or not.
Yes, you can. After importing the CSV file into a raw table (possibly DataTable dt, or BAS_Raw below), which is step [1], in step [2], use ordinary SQL checks and transformations.
SELECT
...
[Hours] = CASE OutTime
WHEN NULL THEN CONVERT( TIME, "00:00" )
ELSE CONVERT( TIME, OutTime - InTime )
END
...
FROM BAS_Raw
Relational Database
The solution is, of course, to:
implement a Relational database, that has both:
Relational tables for all app queries, and
the raw table for the importation only
understand the two-step process that is required for importation of CSV files, including any checking and data transformation, as required.
Note • Content
I have no idea what basid is, therefore I have not modelled it. If you declare what it is, I will model it.
the time difference or hours worked does not need to be stored, because it is easily derived (OutTime minus InTime).
Note • Notation
All my data models are rendered in IDEF1X, the Standard for modelling Relational databases since 1993
My IDEF1X Introduction is essential reading for beginners or novices, wrt a Relational database.

How to decode \xD6 to ö with asp classic?

I get a JSON string that contains the swedish character ö and I put it in the variable "adress", and when I just do response.write adress on the page it is displaying correctly the "ö", but when I try to insert it into my mysql db I get this error.
Microsoft OLE DB Provider for ODBC Drivers fel '80004005'
[MySQL][ODBC 3.51 Driver][mysqld-5.7.21-log]Incorrect string value: '\xD6STERS...' for column 'postadress' at row 1
I have set up the db so it can handle 4 characters and I can insert an emoji and that has 4 characters. The column "adress" is utf8mb4_swedish_ci
And on my server I have set this so that everything should be utf8mb4.
I have this in my mysql configuration file, my.ini
character-set-server=utf8mb4
collation-server = utf8mb4_unicode_ci
init-connect='SET NAMES utf8mb4’
init_connect='SET collation_connection = utf8mb4_unicode_ci’
skip-character-set-client-handshake
But since this isn´t working I guess that I have to decode the variable adress somehow?
So how can I decode a JSON string in asp classic?
Any input really appreciated, thanks.
Would this help...
'remove BOM if present
If (Len(Trim(adress)) > 0) Then
Dim AscValue : AscValue = Asc(Trim(adress))
If ((AscValue = -15441) Or (AscValue = 239)) Then : fileContent = Mid(Trim(adress),4) : End If
End If

Classic ASP + ADODB -- how to see actual query being run (for testing)

I'm using Classic ASP. I have a wrapper function for database queries that accepts a query string and an array of parameters, and auto-creates the proper query object and runs the query. Very handy and has been working great.
Here's my problem: When testing, I often want to see the exact text of the query being passed to SQL. Back in the "bad old days" of assembling queries through concatenation I could just write out the string. Now that I'm using parameterization it's a bit more tricky.
How do I take a peek at the fully-assembled query string just before it's passed to the database connection?
Here is the function I'm using, simplified. (The actual function doesn't assume string, for example.)
Public Function pquery( strQuery, params )
Dim cmd, param, thisParam, rs
Set cmd = Server.CreateObject( "ADODB.Command" )
cmd.ActiveConnection = MyConn
cmd.CommandText = strQuery
If IsArray( params ) then
Dim adVarChar : adVarChar = 200
For Each param In params
Set thisParam = cmd.CreateParameter( "#p", adVarChar, , len( param ), param )
cmd.Parameters.Append thisParam
Next
End If
Set rs = cmd.Execute
Set pquery = rs
End Function
I would consider using Sql Query Profiler, as it'll allow you to view the sql text as well as the values being passed in. It'll allow you to set breakpoints, as well as see how long it takes to run a query. However, this requires the query to be sent to the actual database (you had asked for before).
To do it beforehand, you would need to loop through the parameters collection in the command object, then do a find/replace with the key/value pairs in the command text property. it would be hackish at best, if you can use Profiler, go with that.

Update always encrypted column from decrypted column

I would like to encrypt an existing database column with always encrypted. My project is a ASP.NET project using code first and database is SQL Server. The database has already data. I created a migration to achieve my goal.
First I tried to alter the column type, using the following.
ALTER TABLE [dbo].[TestDecrypted] ALTER COLUMN [FloatCol] [float] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL
I got the following error.
Operand type clash: float is incompatible with float encrypted with (encryption_type = 'RANDOMIZED', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'TestEncrypt')
Then I decided to created another column and migrate the data.
ALTER TABLE [dbo].[TestDecrypted] ADD [FloatCol2] [float] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL
UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = [FloatCol]
And I got the same error.
After I looked at this, I noticed that it is possible to insert data like the following
DECLARE #floatCol FLOAT = 1.1
UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = #floatCol
But if I try to obtain the value from my existing column, it fails.
DECLARE #floatCol FLOAT = (SELECT TOP 1 FloatCol FROM TestDecrypted)
UPDATE [dbo].[TestDecrypted] SET FloatCol2 = #floatCol
The error follows.
Encryption scheme mismatch for columns/variables '#floatCol'. The encryption scheme for the columns/variables is (encryption_type = 'PLAINTEXT') and the expression near line '4' expects it to be (encryption_type = 'RANDOMIZED', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'TestEncrypt').
Does anyone knows how can I achieve my goal?
Update 1
#Nikhil-Vithlani-Microsoft did some interesting suggestions.
Always Encrypted Wizard in SSMS - I would like to achieve my goal with code first migrations, so this idea does not fit.
SqlBulkCopy - It does not work inside migrations, because the new column will only exist after all 'Up' method is run. Therefore we cannot insert data into this column in this way inside this method.
Anyway, his suggestions drove me to another attempt: obtain the decrypted values and update the encrypted column with them.
var values = new Dictionary<Guid, double>();
var connectionString = ConfigurationManager.ConnectionStrings["MainDb"].ConnectionString;
using (var sourceConnection = new SqlConnection(connectionString))
{
var myCommand = new SqlCommand("SELECT * FROM dbo.TestDecrypted", sourceConnection);
sourceConnection.Open();
using (var reader = myCommand.ExecuteReader())
{
while (reader.Read())
{
values.Add((Guid)reader["Id"], (double)reader["FloatCol"]);
}
}
}
Sql("ALTER TABLE [dbo].[TestDecrypted] ADD [FloatCol2] [float] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL");
foreach (var valuePair in values)
{
// The error occurs here
Sql($#"DECLARE #value FLOAT = {valuePair.Value}
UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = #value WHERE Id = '{valuePair.Key}'");
}
In fact, I did not try to create another column and to migrate the data, as mentioned in an example above. I tried it only on SSMS.
And now I got a different error.
Transaction (Process ID 57) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
I tried to do it without encrypting the new column, and it worked properly.
Any idea why this error occurs?
You will have to do the always encrypted related migration outside of entity framework. This blog should help
https://blogs.msdn.microsoft.com/sqlsecurity/2015/08/27/using-always-encrypted-with-entity-framework-6/
If you want to encrypt an existing column, you can use Always Encrypted Wizard in SSMS, or use this article that explains how to migrate existing data.
Also, please note that doing bulk inserts through a C# (.NET 4.6.1+ client) app is supported.
You can do this in c# using SqlBulkCopy specifically using SqlBulkCopy.WriteToServer(IDataReader) Method.
Create a new table (encryptedTable) with the same schema as that of your plaintext table (unencryptedTable) but with the encryption turned on for the desired columns.
Do select * from unencryptedTable to load the data in a SqlDataReader then use SqlBulkCopy to load it to the encryptedTable using SqlBulkCopy.WriteToServer(IDataReader) Method
For example,
Plaintext Table
CREATE TABLE [dbo].[Patients](
[PatientId] [int] IDENTITY(1,1),
[SSN] [char](11) NOT NULL)
Encrypted Table
CREATE TABLE [dbo].[Patients](
[PatientId] [int] IDENTITY(1,1),
[SSN] [char](11) COLLATE Latin1_General_BIN2
ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256',
COLUMN_ENCRYPTION_KEY = CEK1) NOT NULL)
As for why your method does not work,
when you use parameterization for always encrypted, the right hand side (RHS) of the declare statement needs to be a literal. Because the driver will identify the literal and encrypt it for you. So, the following will not work, since RHS is a sql expression and cannot be encrypted by the driver
DECLARE #floatCol FLOAT = (SELECT TOP 1 FloatCol FROM TestDecrypted)
UPDATE [dbo].[TestDecrypted] SET FloatCol2 = #floatCol
Update:
The following code will not work because parameterization for Always Encrypted only applies to SSMS
foreach (var valuePair in values)
{
// The error occurs here
Sql($#"DECLARE #value FLOAT = {valuePair.Value}
UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = #value WHERE Id = '{valuePair.Key}'");
}
However, if you rewrite your code as follows, that should work
foreach (var valuePair in values)
{
SqlCommand cmd = _sqlconn.CreateCommand();
cmd.CommandText = #"UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = #FloatVar WHERE Id = '{valuePair.Key}'");";
SqlParameter paramFloat = cmd.CreateParameter();
paramFloat.ParameterName = #"#FloatVar";
paramFloat.DbType = SqlDbType.Float;
paramFloat.Direction = ParameterDirection.Input;
paramFloat.Value = floatValue;
cmd.Parameters.Add(paramFloat);
cmd.ExecuteNonQuery();
}
Hope that helps, if you have additional question, please leave them in the comments.

Upload image to server using C#/.NET and storing filename in DB

I'm currently using the following snippet to insert data into a table in my database. It works great. But, I want to start adding filename data and not sure how to proceed.
I have the following:
// Create command
comm = new SqlCommand(
"INSERT INTO Entries (Title, Description) " +
"VALUES (#Title, #Description)", conn);
// Add command parameters
comm.Parameters.Add("#Description", System.Data.SqlDbType.Text);
comm.Parameters["#Description"].Value = descriptionTextBox.Text;
comm.Parameters.Add("#Title", System.Data.SqlDbType.NVarChar, 50);
comm.Parameters["#Title"].Value = titleTextBox.Text;
I also have a File Upload option. But, I don't know how to use this to do the following:
move the file to my images directory and
store the filename value in my table.
I have added the correct enctype to my form but now a little lost.
Can someone explain the best way to do this?
Many thanks for any help with this.
To store the file in an images folder, it should be:
FileUpload1.SaveAs(Server.MapPath("~/Images/" + FileUpload1.FileName));
and then add the command parameters in the fileName
comm.Parameters["#FileName"].Value = FileUpload1.FileName;
Note: you must have the FileName field in your DB table.
I suggest storing file in the db too. This will guarantee data consistency.
Add column to the DB. Replace X with the suitable size if the image is less than 8000, or specify varbinary(MAX) if it is not.
alter table Entries
add FileContent varbinary(X) not null
C# code:
byte[] fileContent = yourFileContent;
using(var connection = new SqlConnection(connectionString))
using (var command = connection.CreateCommand())
{
command.CommandText = #"
INSERT INTO Entries (Title, Description, FileContent)
VALUES (#Title, #Description, #FileContent)
";
command.Parameters.AddWithValue("Description", descriptionTextBox.Text);
command.Parameters.AddWithValue("Title", titleTextBox.Text);
command.Parameters.AddWithValue("FileContent", fileContent);
connection.Open();
command.ExecuteScalar();
}

Resources