I have some simple entity which now needs to have a Profile image. What is the proper way to do this? So, it is 1 to 1 relationship, one image is related only to one entity and vice versa. This image should be uploaded through webform together with inserting related entity.
If anyone can point me to the right direction how to persist images to the db and related entity will be great.
Just a side comment: I think is not a good idea to store images in db.
In general is not a good idea store images in db as dbs are designed to store text not big binary chunks. Is much better to store paths for images and have images in a folder. If you want to get sure of 1 to 1 relationship name image with ID of entity (1323.jpg).
If you want to have image paths you should follow some guidelines (In general code defensively):
On upload of image check that image is valid (even made a binary check of image header)
Don't allow to overwrite an existing image in case of a INSERT of a new entity.
Name images as primary key (1.jpg, 2.jpg)
On load of image don't assume that image is going to be there.
Do not allow (if possible) manual interaction with images (No remoting in machine and copying images from one place to other). Manual interaction can cause inconsistencies.
But I assume that for some reason you should do it. So in order to achieve what you want:
DB design
Create a binary column (binary or varbinary) in your table
It is better if you create it in a different table with 1-1 relationship. However the idea is avoiding to load image when hydrating entity. Use a lazy load approach to load your image only when you want.
You have to avoid to load images when you make a big select (for example if you want to load all your entities in a combo avoid SELECT * From whatever) as it will load thousands of images for nothing. As I said this can be done by having images in a different table, or loading only proper columns in SELECT or by making lazy load. (Or even better by NOT having images in DB, only paths)
C# Code
Use BynaryReader to read it
User Byte array to store it
Check this link for code example: http://www.codeproject.com/Articles/21208/Store-or-Save-images-in-SQL-Server
The code is trivial but why the DB?
If this is a website why not save it to a location on disk where you can easily reference it?
Databases are optimised to store data of a known size and relatively small size. Youre image will most likely be more than 8KB in length (mearning its a MAX datatype).
The image will be stored on a separate row/page from your "profile".
Personally I'd save the images in a known folder and use the id for the image name. For profiles that don't have an image and use a standard gif or similar, probably keep it simple / trim by having simlinks/hardlinks of the profile id to the common gif.
public class Profile
{
public int Id {get;}
public string Name {get; private set;}
public Image Picture {get; private set;}
public void Save()
{
using (var connection = new SqlConnection("myconnectionstring"))
using (var command = new SqlCommand("", connection))
{
command.CommandText =
"UPDATE dbo.TblProfile " +
"SET " +
"Name = #name, " +
"Picture = #picture " +
"WHERE ID = #id";
command.Parameters.AddWithValue("#name", Name);
command.Parameters.AddWithValue("#picture", Picture);
command.Parameters.AddWithValue("#id", Id);
command.ExecuteNonQuery();
}
}
}
I think following link would give you the solution,
Upload Image and Save in DB
Related
I have millions of pictures (each picture around 7Kb) located in a folder temp (under Windows Server 2012) and I want to store them in a SQL Server database.
What I am doing so far is:
Searching for files using: foreach (var file in directory.EnumerateFiles())
Reading each file as a binary data: byte[] data = System.IO.File.ReadAllBytes("C:\\temp\\" + file.Name);
Saving each binary data using SQLCommand:
using (SqlCommand savecmd = new SqlCommand("UPDATE myTable set downloaded=1,imagecontent=#imagebinary,insertdate='" + DateTime.Now.ToShortDateString() + "' where imagename='" + file.Name.Replace(".jpg", "") + "'", connection))
{
savecmd.Parameters.Add("#imagebinary", SqlDbType.VarBinary, -1).Value = data;
savecmd.ExecuteNonQuery();
}
Each picture inserted successfully is deleted from temp folder
This kind of fetching for a file and go and store it into database does not take a lot of time because myTable has a clustered index on imagename.
But when we talk about millions and millions of files, it takes a huge amount of time to complete this whole operation.
Is there a way to improve on this way of working? For example, instead of storing file by file, store ten by ten, or thousand by thousand? Or using threads? What is the best suggestion for this kind of problem?
You should think about indexing your image storage by an identifier, not the big nvarchar() field you use for your image name "name.jpg".
It is way more faster to search by an indexed ID.
So i would suggest to split your table in two tables.
The first one holding an primary unique ID (indexed) and the ImageBinary.
The second table holds foreign Key ID reference, insertdate, downloaded, image name (PK if needed and indexed).
By integrating views or stored procedures, you can then still insert/update via a single call to the DB, but read entries by just looking up the picture by ID directly on the first table.
To know which ID to call, you can cache the IDs in memory (and load them from table 2 at startup or so).
This should fasten the reading of pictures.
If your main problem is to bulk insert and update all the pictures, you should consider using a user define table type and bulk merge the data into the DB
https://msdn.microsoft.com/en-us/library/bb675163(v=vs.110).aspx
If you can switch your logic to just inserting pictures, not updating, you could use the .net class "SqlBulkCopy" to fasten things up.
Hope this helps,
Greetings
It sounds like your issue isn't the database, but FileIO finding the files themselves for deletion. I'd suggest splitting the temp file into multiple smaller files. If there's good distribution across the alphabet, you could have a directory for each letter (and numbers if there are some of those as well) and put the files into the directory that matches their first letter. This would make finding and deleting the files much faster. This could even be extended to have a few hundred files using the first 3 characters of the filename. This would help significantly with millions of files.
In my project i have a bit big database that has about 60 tables.
I should save and collect many image files (about 5000) which the average of their size is about 2MB in large.
the estimated size of my database would be 10 GB or even higher than!
Consider these models in code first:
class Document
{
[Key]
public int Id {get;set}
// ...
public virtual ICollection<ImageDocument> Images {get;set;}
}
and
class ImageDocument
{
[Key]
public int Id {get;set}
// ...
public Document Document {get;set;}
}
as you see every Document has some ImageDocuments.
My Solution:
Consider the following two-step:
Add ImageDocuments to Related Document then add the produced
Document by calling Add and SaveChange methods from EntityFramework
DbContext.
Calling a created stored procedure for every ImageDocuments of
related Document. The called stored procedure use bcp command to
extract image file from database and save it in specific path of
server, then removes the ImageDocument's data from database.
It works but i have some problems in this way:
I' can not create backup integrated file.
Atomicity-violation because of my save transaction gets broken to
some small transactions.
Consistency-violation. maybe in calling stored procedure system
fall in crash.
Durability-violation. because of deletion ImageDocument record to
release database space.
now my question is that, are there any better solution to do this and solve the problems?
it would be great if we can create a file field in SQL server that maintain the content in files separated database file.
If you are using SQL Server, you should be using FileStreams. Straight to disk, via SQL proxy.
http://msdn.microsoft.com/en-us/library/gg471497.aspx
I'm fairly new to C# .Net. We're being taught it at University and are using Visual Studio to create windows forms. As a new part to the subject we're using databases, tables and datasets.
I opened a new Windows Form project and immediately added a new database to it. The table I want to create will have 2 columns - ImageID and the image itself. In what way do i add the image in to the box? I've tried full path, relative path and dragging the image in, but whatever I do i get the same error message....
Invalid Value
The changed value in this cell was not recognized as being valid.
.Net Framework Data Type: Byte[]
Error Message: You cannot use the result pane to set this Field data to
values other than NULL
Type a value appropriate for the data type or press ESC to cancel the
change
How can I have images in there? I just don't know how to use the image data type within the table. Any help is much appreciated.
A simpler approach is to store the image in the file system and only its path in the database. Basically you define a base folder:
string baseFolder = "c:\Program Files\MyApp\Images";
And use it to store relative paths in the database:
INSERT INTO ImagesTable (Name, Path)
Values ('German Shepherd', 'Dogs\german-shepherd.jpg')
Then, when you need to retrieve the image, you can do it like this:
string path = Path.Combine(baseFolder, 'Dogs\german-shepherd.jpg');
Image img = Image.FromFile(path);
In the following SO question you can find more information about the pros and cons of this approach:
Should I store my images in the database or folders?
you can store images in sql server 2008. Just create database table having column datatype "image".
now from .net code use the file upload control to select the image file and then convert the image parameter into byte[] before inserting image data into the database.
In my database I have stored images in the "image" data type, showing up as a binary code.
I now want to retrieve all images from one column and diplay them on a asp.net page with C#.
databaseDataContext db = new databaseDataContext();
var images = from Picture p in db.Pictures
select p.pictureThumb;
then I use this:
foreach (Picture p in images)
{
galleryImages.Controls.Add(p);
}
But that doesn't work because Binary can't be converted to Picture. I have Googled this and found that I have to cast to Byte and then to image? I can't find examples on how to do that though.
This should do it:
databaseDataContext db = new databaseDataContext();
var images = from p in db.Pictures
select Image.FromStream(new MemoryStream(p.pictureThumb.ToArray());
foreach (Image image in images)
{
galleryImages.Controls.Add(image);
}
Note that Image.FromStream takes ownership of the stream - it's only a MemoryStream anyway, but do make sure that you dispose of the image, for various reasons.
EDIT: Ah... I hadn't realised that this was for ASP.NET. That makes things harder - because the HTML is going to contain URLs, and then you'll need to be able to fetch the data later.
Does a picture have an ID of some kind? If so, fetch that instead of the actual data, and then set up a URL which is able to serve any thumbnail based on the ID (by fetching it from the database and just serving it with an appropriate content type).
i'm working on Linq To Sql,WPF and i have a database now i need to save some picture in the database but i don't know which is the correct datatype to save the pictures Database(this database would be connect from 10 users in the same time).
Can you point me in the right way to overcome this step?
If i didn't wrong it is not a good idea to save pictures in the database but if you can advice me a better method i will apply it.
Thanks so much for your time.
Nice Regards
You can use a 'varbinary(MAX)' or 'image' column type. Linq2Sql will auto-generate a class that uses a Binary object to wrap your image. The Binary object is just a wrapper around a byte[].
myObject.Image = new Binary(imageByteArray);
Store your picture as a blob, the variable defined in your class containing the image could be a byte[] stream. Alternatively you just store a reference to the picture in the database and store the image on a file server.
Typically you will use a varbinary(max) -or less than max- on the database side and you will use a byte[] type in your class.
There's a lot of heated debates that occur when people talk about this, I would like to note that you might want to consider storing that path to a network folder in the database.
The disadvantage of storing the actual image in the database is that all those bytes have to get sent back and forth through a sql query and if those images are large you will be increasing the size of your db substantially. along with the weird things that were mentioned above.
Anyways I don't want to open up a can of worms, just wanted to show an alternative.
UPDATE:
Something like this:
public partial class LinqClass
{
public string ImagePath { get; set; }
public System.Drawing.Image Picture
{
get
{
return System.Drawing.Image.FromFile(ImagePath);
}
}
}
where ImagePath is the actual column in the db that you are saving the file path to. This doesn't have the code to save the file (something like File.Save(ImagePath) etc. but it's a start.
I've never done it with Linq, but we used a b64 conversion for the image, then a clob datatype. Then reverse the b64 when you want to view the image.