Storing binary data on SQL Server

Mon 20 February, 2006

On my current project, an ASP .NET application, the customer expressed the desire to save in the database letters, based on Word templates and automatically generated by the application. Being official government letters, if in the future you need print a copy of that document it has to be exactly the same document, although some of the data on which the document was originally based could have changed. So they need them to be stored in the database exactly as they were generated once. Therefore, we need a way to store the Word files as a binary format, as a data stream to be able to reload them again in the future and have the exact same document.

Please note: it’s not the objective of this post to show how to create Word files from C#. You can use for it Word automatization, or third-party components such as the excellent ones made by Aspose.

Let’s create a simple SQL Server table to store the documents. For the sake of this example, we call it BinaryDocs, and it will have the following design:

Field Type Nullable?
DocId Int (identity) No
Document Image No
DocName VarChar(100) No

Pretty self-explanatory: DocId is an autonumeric primary key field. Document is the one where the bit stream is going to be stored, that is the proper Word document. DocName will store the original name the document had when it was generated.

After that will create an stored procedure, UploadDoc, which will handle the actual insertion of records on BinaryDocs:

CREATE PROCEDURE UploadDoc(@doc AS Image, @name AS VarChar(100)) 
AS
INSERT INTO 
BinaryDocs (Document, DocName) values (@doc, @name)
GO

Colorized by: CarlosAg.CodeColorizer

Quite self-explanatory too: a simple INSERT INTO instruction which receives an Image and a VarChar fields as parameters.

And this is the method that stores the data on the database:

private void saveDocToDB(string sPath, string sFile)
{
    
//New FileStream object for reading the Word file as a binary stream
    
System.IO.FileStream  fs = new FileStream(sPath + sFile,
        System.IO.FileMode.Open)
;
    
//On this byte array we’ll store the binary data read by fs.
    
Byte[] data = new byte[fs.Length];
    
//Like this
    
fs.Read(data, 0, Convert.ToInt32(fs.Length));
    
//Open up a SQL Server connection. In this particular case the database
    //connection string is recovered from the web.config file
    //using the ConfigurationSettings class
    
SqlConnection cnn =
       new 
SqlConnection(ConfigurationSettings.AppSettings["DBConnection"]);
    
cnn.Open();
    
//An StoredProcedure SqlCommand object to invoke the
    //UploadDoc stored procedure created earlier
    
SqlCommand cmd = new SqlCommand("UploadDoc", cnn);
    
cmd.CommandType CommandType.StoredProcedure;
    
//This are the expected parameters
    
cmd.Parameters.Add("@doc", data)//the Word file data
    
cmd.Parameters.Add("@name", sFichero)//and its name
    //We execute the stored procedure, which inserts a new
    //row on BinaryDocs which the our data
    
cmd.ExecuteNonQuery();
    
//Let’s close the connection and the file (althought   //we could have closed the file earlier)
    
cnn.Close();
    
fs.Close();
}

Colorized by: CarlosAg.CodeColorizer

Now let’s take a look at the method to retrieve that stream from the database and display it as a Word file:

private void getDocFromDB()
{
    
//Let’s open the connection, exactly as before
    
SqlConnection cnn =
        new 
SqlConnection(ConfigurationSettings.AppSettings["DBConnection"]);
    
cnn.Open();
    
//This is the command to open the record we want. For this
    //example we open the first record, you should have to modify this
    //code and give the method a parameter stating which record
    //to retrieve from the database.
    
SqlCommand comm = new SqlCommand("SELECT * FROM BinaryDocs " +
        " WHERE docId = 1"
, cnn);
    
comm.CommandType CommandType.Text;
    
SqlDataAdapter da = new SqlDataAdapter(comm);
    
DataSet ds = new DataSet("BinaryData");
    
da.Fill(ds);
    
//We create a byte array which will have the bytes stored on the
    //table’s Document field
    
byte[] bits ((byte[])(ds.Tables[0].Rows[0].ItemArray[1]));            
    
cnn.Close();
    
//now let’s save that data stream as a normal file on the HDD,
    //a temporal file you could later delete.
    //For avoiding possible concurrency problems,
    //we generate a unique name for the file.
    
string sFile "tmp" + makeFileName() + ".doc";
    
//A new FileStream object, which will be the one to create
    //the file with the specified name.
    
FileStream fs = new FileStream(Server.MapPath(".") +
        @"\GenDocs\" 
+ sFile, FileMode.Create);
    
//And we save on disk the byte array which contains the Word data
   
fs.Write(bits, 0, Convert.ToInt32(bits.Length));
    
fs.Close();
    
//For displaying the file, we use a Javascript function
    //called showFile, which simply loads the specified
    //file, and we show it on a pop-up
   
string script "<script languaje=’javascript’> ";
    
script +"showFile(’GenDocs/" + sFile + "’) ";
    
script +"</script>" + Environment.NewLine;
    
Page.RegisterStartupScript("showFile",script);
}

Colorized by: CarlosAg.CodeColorizer

And that’s it. With this last method the Word file is loaded from the database as a byte stream and it’s displayed on a popup browseer window.

Just for the sake of completion, this is the Javascript function that loads the generated file from the database:

<script language="javascript">
function showFile(sFile) {
window.open(sFile,null,"directories=no,height=600,
    width=800,left=0,top=0,location=no,menubar=yes,
    status=no,toolbar=yes,resizable=yes"
)
document.forms(0).submit();
}
</script>

Colorized by: CarlosAg.CodeColorizer

And this is the method makeFileName(), which uses the server’s tick count for creating randomly unique filenames.

private string makeFileName()
{
    
int lastTick 0;
    while
(lastTick==Environment.TickCount) 
    {
        System.Threading.Thread.Sleep(
1);
    
}
    lastTick
=Environment.TickCount;
    return 
DateTime.Now.ToString("yyyyMMddhhmmss") + "." +
        lastTick.ToString()
;
}

Colorized by: CarlosAg.CodeColorizer

Comments »

The URI to TrackBack this entry is: http://codecruncher.blogsome.com/2006/02/20/storing-binary-data-on-sql-server/trackback/

No comments yet.

RSS feed for comments on this post.

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>