Adding and Reading files from SQL Server 2008 Filestream
Few days back I wrote a Post about FileStream feature in SQL 2008.(Filestream in SQL Server 2008). In this post I am trying to write the how to manage or use the FileStream feature from .Net managed code.
For adding a File to FileStream enabled table, we are using a new Data Type, SqlFileStream, which comes with .Net 3.5.
I am using the Table structure as in the Previous post, as the example, that Table contains 3 columns, FileID(PK), FileName and FileContents.
For inserting a File you need to begin a transaction, insert an row in to the Table, without the contents of the File, for reading the Transaction context. Then using FileStream, write the contents of the File to the Row. After writting the stream, need to commit the transaction to update the file to the Database. I am writing the code in VB.Net.
Code for saving the File to the Database
Private Sub AddFile(ByVal fileName As String)
Dim _Transaction As SqlTransaction
Dim _Command As SqlCommand
Dim _DataReader As SqlDataReader
Dim _Connection As SqlConnection
Dim _FileId As String
Dim _FilePath As String
Dim _FileNameParameter As SqlParameter
Dim _FileIdParameter As SqlParameter
Dim _FilePathParameter As SqlParameter
Dim _SQLFileStream As SqlTypes.SqlFileStream
Dim _Context As Byte()
Try
_Connection = New SqlConnection("Server=.\SQL2008;Integrated Authentication=SSPI;Database=FileSystemExample;")
_Connection.Open()
_Transaction = _Connection.BeginTransaction()
_Command = New SqlCommand("xsp_InsertFile", _Connection, _Transaction)
_Command.CommandType = CommandType.StoredProcedure
_Command.Parameters.AddRange(New Object() {_FileIdParameter, _FileNameParameter, _FilePath})
_FileId = Guid.NewGuid().ToString
_FileIdParameter = New SqlParameter("@FileId", SqlDbType.UniqueIdentifier)
_FileIdParameter.Value = _FileId
_FileNameParameter = New SqlParameter("@FileName", SqlDbType.VarChar)
_FileNameParameter.Value = fileName
_FilePathParameter = New SqlParameter("@FilePath", SqlDbType.VarChar)
_FilePathParameter.Direction = ParameterDirection.Output
_DataReader = _Command.ExecuteReader(CommandBehavior.SingleRow)
If _DataReader.HasRows Then
_FilePath = _DataReader("FilePath").ToString
End If
If Not _DataReader.IsClosed Then
_DataReader.Close()
End If
_Command = New SqlCommand("SELECT GET_FILESTREAM_TRANSACTION_CONTEXT() FROM SQLFileSystem", _Connection, _Transaction)
_Context = _Command.ExecuteScalar()
_SQLFileStream = New SqlFileStream(_FilePath, _Context, FileAccess.Write)
_SQLFileStream.Write(_Context, 0, _Context.Length)
_SQLFileStream.Close()
_Transaction.Commit()
Catch ex As Exception
If _Transaction IsNot Nothing Then
_Transaction.Rollback()
End If
Finally
If _Connection IsNot Nothing Then
_Connection.Close()
End If
End Try
End Sub
Code for reading the File from the Database
Private Function ReadFile(ByVal fileId As String) As Byte()
Dim _Transaction As SqlTransaction
Dim _Command As SqlCommand
Dim _DataReader As SqlDataReader
Dim _Connection As SqlConnection
Dim _FilePath As String
Dim _FileIdParameter As SqlParameter
Dim _SQLFileStream As SqlTypes.SqlFileStream
Dim _Context As Byte()
Try
_Connection = New SqlConnection("Server=.\SQL2008;Integrated Authentication=SSPI;Database=FileSystemExample;")
_Connection.Open()
_Transaction = _Connection.BeginTransaction()
_Command = New SqlCommand("SELECT FileStreamData.PathName() AS [FilePath],GET_FILESTREAM_TRANSACTION_CONTEXT() AS [Context] FROM SQLFileSystem WHERE FileId=@FileId", _Connection, _Transaction)
_Command.CommandType = CommandType.StoredProcedure
_FileIdParameter = New SqlParameter("@FileId", SqlDbType.UniqueIdentifier)
_FileIdParameter.Value = fileId
_Command.Parameters.Add(_FileIdParameter)
_DataReader = _Command.ExecuteReader(CommandBehavior.SingleRow)
If _DataReader.HasRows Then
_FilePath = _DataReader("FilePath").ToString()
_Context = TryCast(_DataReader("Context"), Byte())
End If
_SQLFileStream = New SqlFileStream(_FilePath, _Context, FileAccess.Read)
_SQLFileStream.Read(_Context, 0, _Context.Length)
_SQLFileStream.Close()
_Transaction.Commit()
Catch ex As Exception
If _Transaction IsNot Nothing Then
_Transaction.Rollback()
End If
Finally
If _Connection IsNot Nothing Then
_Connection.Close()
End If
End Try
Return _Context
End Function
Stored Procedure
CREATE PROCEDURE xsp_InsertFile ( @FileName VARCHAR(255), @FileId UNIQUEIDENTIFIER, @FilePath VARCHAR(MAX) OUTPUT) AS BEGIN SET NOCOUNT OFF INSERT INTO SQLFileSystem (FileId, FileName) VALUES(@FileId, @FileName) SELECT @FilePath = SystemFile.PathName() from SQLFileSystem where FileId = @FileId END
Note: I didn’t tested the code. Please let me know if you found any issues in the implementation.
FILESTREAM in SQL Server 2008
SQL Server 2008 comes with lots of new features compared to the previous versions of SQL Server. One of the new feature is FileStream, which allows storage of and efficient access to BLOB data using a combination of SQL Server 2008 and the NTFS file system.
You can get more details about this in MSDN : FILESTREAM Storage in SQL Server 2008
Enable Filestream in SQL Server
By default the Filestream feature will be disabled. You can enable the filestream using SQL Server Configuration Manager under SQL Server 2008 > Configuration Tools. In this you will get all the SQL Server services. Select the Properties of the instance and select the Tab “FileStream”, from that you can enable the FileStream, you can also specifiy the instance name also.
You can also do it via T-SQL statement also
EXEC sys.sp_configure N'filestream access level', N'2' RECONFIGURE
After doing this SQL Server will create a shared folder in your machine(or in Server) with the instance name specified. (Or it will create the Windows Share name we are specifying in the textbox) Only SQL Server can access the contents.
You can check this via command prompt, using “Net Share”, you will get an output like this.
Using Filestream in the Database.
For using Filestream in your database you have to add file group in New Database screen.
Or you can do this via TSQL like this
CREATE DATABASE FileStreamDemo
ON PRIMARY
(NAME = FileStreamDemo,
FILENAME = N'D:\DB\FileStreamDemo_data.mdf'),
FILEGROUP FileStreamFileGroup CONTAINS FILESTREAM
(NAME = FileStreamDemo,
FILENAME = N'D:\DB\FileStreamDemo')
LOG ON
(NAME = 'FileStreamDemo_log',
FILENAME = N'D:\DB\FileStreamDemo_log.ldf');
go
After doing this, SQL Server will create Folder in “D” drive, with name FileStreamDemo under DB directory. This FileStreamDemo folder will contains two files
- filestream.hdr – This is the FILESTREAM metadata for the data container.
- The directory $FSLOG. This is the FILESTREAM equivalent of a database’s transaction log.
Creating a Table with FILESTREAM Data
You can create a Table for consuming FileStream like this.
CREATE TABLE SQLFileSystem ( FileId UNIQUEIDENTIFIER ROWGUIDCOL UNIQUE DEFAULT NEWID() PRIMARY KEY, FileName VARCHAR(255), FileContents VARBINARY(MAX) FILESTREAM NULL default (0x) )
Thats it, you have created SQL Server Database and Table with Filestream.
Using Multiple Active Result Sets (MARS)
Today, Aneesh (the guy who blogged about SQL Encryption few months back in dot net thoughts), told me about the Multiple Active Result Sets (MARS), a new option available in SQL Server 2005, which helps developers to maintain Multiple active Statements on a connection. When using SQL Server default result sets, the application had to process or cancel all result sets from one batch before it could execute any other batch on that connection. SQL Server 2005 introduced a new connection attribute that allows applications to have more than one pending request per connection, and in particular, to have more than one active default result set per connection.
The MARS feature is disabled by default. It can be enabled by adding the “MultipleActiveResultSets=True” keyword pair to your connection string, as below:
string connectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=master;Integrated Security=SSPI;MultipleActiveResultSets=True";
Without MARS
string sql1 = "SELECT * FROM gotMenus";
string sql2 = "SELECT * FROM gotUsers";
using (SqlConnection conn = new SqlConnection(@"Data Source=.\SQLEXPRESS; Initial Catalog=GotCms;Integrated Security=SSPI;"))
{
conn.Open();
using (SqlCommand cmd1 = new SqlCommand(sql1, conn))
{
SqlDataReader dr1 = cmd1.ExecuteReader();
}
using (SqlCommand cmd2 = new SqlCommand(sql2, conn))
{
SqlDataReader dr2 = cmd2.ExecuteReader();
}
}
The above code will throw an invalid operation exception(There is already an open DataReader associated with this Command which must be closed first),unless you close the dr1 using dr1.Close();
Or you can modify the connection string like the below.
With MARS
string sql1 = "SELECT * FROM gotMenus";
string sql2 = "SELECT * FROM gotUsers";
using(SqlConnection conn = new SqlConnection(@"Data Source=.\SQLEXPRESS; Initial Catalog=GotCms;Integrated Security=SSPI;MultipleActiveResultSets=True"))
{
conn.Open();
using (SqlCommand cmd1 = new SqlCommand(sql1, conn))
{
SqlDataReader dr1 = cmd1.ExecuteReader();
}
using (SqlCommand cmd2 = new SqlCommand(sql2, conn))
{
SqlDataReader dr2 = cmd2.ExecuteReader();
}
}
It will work fine without any issue.
You can get more detailed information about MARS from these links
http://technet.microsoft.com/en-us/library/ms345109
(SQL.90).aspx
http://msdn.microsoft.com/en-us/library/ms131686.aspx
Tip : The server connection attribute, used in SQL Server connection string is optional. Only Database name and Security attributes required as mandatory for establishing a connection to SQL Server.
SqlCeExplorer – Alpha release
Hi All
My first open source project, SQLCEExplorer, a small utility to explore the SQL CE Database, yesterday uploaded the Alpha version to codeplex.
You can get this one from sqlceexplorer on Codeplex
Comments / Feedbacks : Welcome ![]()
You can send mail to : anuraj dot p at live dot com
Changing authentication mode and enabling sa using osql
Changing authentication mode and enabling sa using osql Few days back, I tried to install SQL Server Management studio express in my machine, because of some strange reasons, it is getting rollbacked everytime. I don’t know why I am getting that error. Then for some development purpose I have to use sa, or sql authentication in SQL 2005 instead of Windows. If I do have SQL Server Management studio, it is pretty easy job. But using osql and registery settings we can achive the same.
- Changing SQL Server authentication mode. Note: Backup registry before making any changes.
- Open Registry editor using RegEdit command.
- Goto the key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.x\MSSQLServer, locate a subkey with name “LoginMode”.
- If LoginMode subkey is 1, then the SQL Server is confingured to Window authentication, and if it is 2 then it is Mixed mode authentication.
- Go to services and stop all the sql server related services, before making the change.
- Double click on the LoginMode subkey, in the DWORD Editor dialog, set the value as 2.
- Restart all the SQL related services
This procedure will change the authentication mode to mixed mode, so that we can use “sa” user for login. But by default “sa” may not be enabled.
- Enabling sa account
- Go to command promprt type “osql -S localhost\SqlExpress -E”
- This will authenticate you with windows authentication, to the local sql express. You will get 1> sign for accepting the Sql commands
- You need to give sa a stong password because of security reasons. You can do this by this was “sp_password @old = null, @new = ‘complexpwd’, @loginame =’sa’; ” and type “go”
- Type “ALTER LOGIN sa ENABLE” and “GO”, will enable the sa account.
- Type quit, and try login using sa, like this “osql -S localhost\SqlExpress -U sa -P mypassword”. If everything worked fine, you will get a prompt 1>
You can also get information on these link from Microsoft.



