I'm trying to serve image data stored in a VARBINARY(MAX) field in the database using ASP.Net. Right now, the code is filling a data table, then pulling the byte array out of the DataRow and pushing the byte array into the response. I'm wondering if there's a way to more-or-less stream the data from the SQL Server into the response without having to marshal around these huge byte arrays (since the images are large, they cause OutOfMemoryExceptions). Is there a class/mechanism for that?
The current code looks more or less like:
DataTable table = new DataTable();
SqlDataAdapter adapter = new SqlDataAdapter(commandText, connectionString);
adapter.Fill(table);
DataRow row = table.Rows[0];
byte[] imageData = row[0] as byte[];
if(imageData != null)
{
Response.Clear();
Response.BinaryWrite(imageData);
Response.End();
}
Thanks in advance - any help is appreciated.
See Download and Upload Images from SQL Server for an article covering the topic, including efficient streaming semantics. You must use a SqlDataReader opened with CommandBehavior.SequentialAccess:
SequentialAccess Provides a way for
the DataReader to handle rows that
contain columns with large binary
values. Rather than loading the entire
row, SequentialAccess enables the
DataReader to load data as a stream.
You can then use the GetBytes or
GetChars method to specify a byte
location to start the read operation,
and a limited buffer size for the data
being returned.
The linked article provides full code for creating a Stream backed by an SqlDataReader, you can simply Stream.CopyTo(HttpResponse.OutputStream), or use a byte[] chunked copy if you don't have .Net 4.0 yet.
This follow up article explains how to use a FILESTREAM column for efficient streaming of large VARBINARY data in and out of the database.
#Remus's answer above is out-of-date since .NET 4.5 introduced first-class SqlClient Streaming. It's no longer necessary to access the SqlDataReader's GetBytes() methods to get a stream from a SQL Query.
Now you simply call SqlDataReader.GetStream(int) to get a stream over a blob column.
Related
What I want to do:
Write a lot of small (average 500-1000 bytes) blob-like objects that I get from a queue to a SQL Server database, to a table that has a bigint primary key, and the data as varbinary column.
Currently I do it this way:
get some object from a concurrent queue
create a new memory stream, and a new binary writer
serialize the object into the memory stream with the Write... command from the writer
call stream.ToArray() and set the result as the parameter value of the SQL command
execute the command
This creates a lot of small byte arrays which will become garbage immediately after the write. I would like to avoid this, and only create one byte array, always write & read from that array.
But it seems like the SqlCommand objects internally copy the buffer if you pass a byte array.
Also the problem of reusing the same byte array over and over is that I cant tell the SqlCommand how many bytes it should write to the table (each object will have a slightly different length)
Any idea how to do this without writing my own SQL implementation ?
I'm using C# 4.5 Framework and MySql
MySqlDataReader reader = Command.ExecuteReader();
if (reader.Read())
{
byte[] ReturnImage = reader["Photo"] as byte[];
MemoryStream ms = new MemoryStream(ReturnImage);
Image Photo = Image.FromStream(ms); //Error is in this statement!!
}
When this stmt is executed the following error displays "Parameter is not valid"
I couldn't find the answer from the web.. Somebody Pls help..
The most likely cause here is that the contents of the longblob are not the raw image bytes. Rather than go around in circles, the first thing to do is to: compare them. For example, you say (comments) that the data came from a jpg file, via OpenFileDialog. In that case, compare them. Check that you have successfully stored and retrieved the image.
Let's suppose that the file in question is c:\Some\Photo.jpg - stored etc per whatever process. In that case, you should be able to check the contents are the same. Until the following reports success, all bets are off:
byte[] original = File.ReadAllBytes(#"c:\Some\Photo.jpg");
byte[] ReturnImage = reader["Photo"] as byte[];
if(Convert.ToBase64String(original) == Convert.ToBase64String(ReturnImage)) {
Console.WriteLine("Success; the contents match");
} else {
Console.WriteLine("Failure; the contents are different");
}
If this reports "Failure; the contents are different", then the error is most likely in one of:
the code where you prepare the image to be stored (populating parameters etc)
the stored procedure that does the storage
the code that fetches the image back from the database
If this reports "Success; the contents match": then and only then is it meaningful to look at the code that attempts to load the Image. In this scenario, and assuming that c:\Some\Photo.jpg loads in most other image loading tools ("paint", etc) - then it is possible that Image doesn't recognise the subformat. But my guess is that it is going to say "Failure; the contents are different".
Note that Convert.ToBase64String here is used solely as a lazy way to check binary equivalence. You wouldn't use it like this in production code, but it is fine for this purpose.
I am creating a asp.net app in C# .net 4.0 using VS 2008 & MS SQL Server 2008 R2. I want to upload image(.bmp) for each order and store it in DB corresponding to its order. It must be retrieved when View functionally is requested.
My requirement is to store images and retrieve it. Number of orders might be around 50 orders each day. It might be over be overhead as number increases. Please suggest me efficient method to overcome memory issue.
Well, you can either store them in the database as varbinary(max) or store a URL to a file. If you have 50 images a day, it might be better to go for the file and URL method.
There's advantages and disadvantages to both. But as you said you want to store them in the DB:
I think one big one on the database storage is that no one can just come along and rename / delete the folder, or mess with the files in it.
Another good point for the database is that you can restore a database and not have to try to keep the keep the folder in sync.
You can use an ImageConverter to convert between an array of bytes to an image and back
var stream = new MemoryStream();
var imageData = DataAccess.GetImageData(); //Returns a System.Linq.Binary for me
var imgageBinaryData = imageData.ImageData.ToArray(); //This returns a byte array
var img = Image.FromStream(stream); //Here is your image ready for displaying
Then to go back something like
var stream = new MemoryStream();
img.Save(stream,System.Drawing.Imaging.ImageFormat.Jpeg);
var myBytes = ms.ToArray();
Edit: Oh and storing them as Jpeg rather than Bmp will save a lotta space
i need to get an image from client and displayed(preview) before saving it into database..and then should be saved in database after getting preview..?
Is is possible in Asp.net using c#... ?
Yes, it's possible.
If you are using MS SQL Server, declare a field in your table as type "image" or "varbinary". Varbinary limits the number of bytes stored depending on your version of SQL Server, so "image" is the way to go. After all, you are storing an image ;)
Then store your image as a byte array in that field.
Without seeing some code, it's hard to give code samples. Are you using SqlCommand directly, Linq2Sql, Entity framework or NHibernate to communicate with your database?
[Edit: Linq2Sql sample]
On your linq object you will have a propery called Image, if that's what you named your image/varbinary column. To assign it do this:
// assign data
byte[] imageByteArray = ...some byte data...;
myObject.Image = new Binary(imageByteArray);
// save to db
dataContext.YourTable.InsertOnSubmit(myObject); //YourTable is the name of your actual table class
dataContext.SubmitChanges();
where imageByteArray is a byte[] holding your image.
The Binary object is a wrapper around a byte[].
Does any one know of an example on how to store an image in a SQL Server CE database?
What data type should the column be? (I am guessing binary.)
I use Linq-To-Datasets. Is it possible using that to put the image into the database and pull it out again later?
Thanks for any advice.
Here is how I did it:
MemoryStream stream = new MemoryStream();
myBitmapImage.Save(stream, ImageFormat.Png);
myInsertLinqToDataSetRow.IMAGE_COLUMN = stream.ToArray();
To load it back out again I did this:
MemoryStream stream = new MemoryStream(myLinqToDataSetRow.IMAGE_COLUMN);
myBitmapImage.SignatureImage = new Bitmap(stream);
I found a page on MSDN that said that the Image column type is going away and that you should use varbinary(MAX). Max is not supported on SQL Server CE so I did varbinary(8000).
LATER NOTE: while varbinary(max) is not supported on SQL Server CE. Varbinary(8000) is not big enough for many images. I did end up using the Image type even though it is planned to be deprecated. Once ms offers a resonable alternitive on the mobile platform I will consider switching.
Here is an MSDN article explaining how:
http://support.microsoft.com/kb/318639
Unfortunately, the example is in VB, but I am sure that you can get the idea of how to do it.
I would say the binary datatype would be fine for storing the images.
Why aren't you using the Image type? Here is a description of types, supported by the sql server ce.
Image - Variable-length binary data with a maximum length of 2^30 – 1 (1,073,741,823) bytes.
This is not a direct answer to your question, but I've had good success storing the image's filepath (example: C:\images\image1.png) as a string value in the database instead of the raw image.
One advantage to this is that it keeps the database size smaller.
Another is that multiple tables can point to the images without storing multiple copies of the image.