Developing Matt

My Technical Journal

Quick and dirty: Sql Filestream Object – creating, inserting, displaying

leave a comment »

There are a lot of sites on setting up the filestream object in sql (I’ll let you discover that yourself), but I had a hard time finding a good source of information for utilizing it in a web page.  Also, there are also a lot of pages out there that tell you more information than you need to know.  For instance I found many pages telling me that I had to utilize the rowguid in all kinds of ways….that I had to run an insert first to the table to get the rowguid and then get that gowguid and use it in some complicated way…but in the end I never used it in my application. 

I tried to strip out as much as I could to get the end result for this post.

1. SETTING IT UP (DATABASE)

My table has other fields, but I stripped them out.  The only one not necessary is the DocTitle and I suppose the AttachmentID. RowGuid is required by the sql engine for the filestream object but I never use it except here to create the table:

CREATE TABLE [dbo].[tblAttachment](
    [AttachmentID] [int] IDENTITY(1,1) NOT NULL,
    [DocTitle] [varchar](50) NULL,
    [RowGuid] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
    [FileObject] [varbinary](max) FILESTREAM  NULL

)

My procedure looks something like this:

CREATE PROCEDURE [dbo].[pr_ins_attachment]
@doctitle varchar(50)=null
,@fileobject varbinary(max)=null

AS
BEGIN
   SET NOCOUNT ON;
   INSERT INTO tblAttachment( DocTitle, fileobject)
   values( @DocTitle,  @fileobject)
END

My procedures for getting the attachments looks something like this (the first one gets all of them for display on the web page.  The second one gets just the file for when the user clicks the link.

CREATE PROCEDURE [dbo].[pr_get_attachment]
AS
BEGIN
SELECT DocTitle, AttachmentID FROM tblattachment
END

CREATE PROCEDURE [dbo].[pr_get_attachmentfile]
@attachmentid int
AS
BEGIN
select FileObject from tblattachment
where AttachmentID = @attachmentid

END

2. SETTING IT UP TO INSERT(WEB PAGE)

My markup on my aspx page looks like this:

<asp:FileUpload ID="fileupload" runat="server" Width = "500px" /><br />
&nbsp;<asp:Button ID="cmdInsertAttachmentFie" runat="server" Text="OK" Width="55px" />
<br />

Side note: I wanted to allow up to 10 MB files, so I added the following to the web.config to allow this (per link) :

<configuration>
   <system.web>
       <httpRuntime maxRequestLength="15360"/>
    </system.web>
</configuration>

3. INSERTING TO DATABASE

My On_click method on my aspx page calls the following method.  Note that I am reading the input stream of the file which enables us to get the bytes of data.  It looks something like this:

Protected Sub cmdInsertAttachmentFie_Click(sender As Object, e As EventArgs) Handles cmdInsertAttachmentFie.Click
    If fileupload.HasFile Then
        Dim filebytes(fileupload.PostedFile.ContentLength) As Byte
        fileupload.PostedFile.InputStream.Read(filebytes, 0, fileupload.PostedFile.ContentLength)
        Dim sFileName As String = fileupload.FileName
        m.InsertAttachment(sFileName, filebytes)

    End If
End Sub

The on_click calls the method to insert in the database.  It sits in the following routine and looks something like this:

Public Sub InsertAttachment(ByVal DocTitle As String, ByVal filebytes As Byte())
    ‘this method is for inserting a file into the database
    Dim sConn As String
    sConn = "server=databaseserver;database=databasename;UID=username;Password=password"
    Dim con As New SqlClient.SqlConnection(sConn)
    con.Open()
    ‘Create a command to execute      
    Dim cmd As New SqlClient.SqlCommand()
    cmd.Connection = con
    cmd.CommandText = "pr_ins_attachment"
    cmd.CommandType = System.Data.CommandType.StoredProcedure
    ‘ Add Parameter Values      
    cmd.Parameters.AddWithValue("@doctitle", DocTitle)
    cmd.Parameters.Add("@fileobject", System.Data.SqlDbType.VarBinary)
    cmd.Parameters("@fileobject").Value = filebytes

    ‘Execute Command      
    cmd.ExecuteNonQuery()
    ‘clean up      
    con.Close()

End Sub

4. DISPLAYING THE ATTACHMENTS

I’m storing all my attachments in a grid.  Here are the columns for my grid.  I’m using an Infragistics 3rd party  grid, but asp.net’s grid would work fine, but some of the markup will be a little different.  I’ve colored below what I’m doing.  Basically I wanted to put the attachment on a link for the user to click:

<Columns>
<ig:BoundDataField DataFieldName="AttachmentID" Hidden="True" 
    Key="AttachmentID"></ig:BoundDataField>
<ig:TemplateDataField Key="linkbuttontemplate">
    <Header Text="Attached Documents" />
    <ItemTemplate>
        <asp:LinkButton runat="server" ID="MyLinkButton" Text='<%# DataBinder.Eval(CType
             (Container, Infragistics.Web.UI.TemplateContainer).DataItem, "DocTitle") %>’
             OnClick="OpenDocument" CommandArgument='<%# DataBinder.Eval(CType(Container,
             Infragistics.Web.UI.TemplateContainer).DataItem, "link") %>’
            CommandName='<%# DataBinder.Eval(CType(Container,
            Infragistics.Web.UI.TemplateContainer).DataItem, "DocTitle") %>’ />
   </ItemTemplate>
</ig:TemplateDataField>
</Columns>attachmentsgrid

When they click the link the following occurs in code-behind.  Rick Strahl tells me that it is best not to use the ‘application/octet-stream’ content type (link), but I didn’t take his advice.  I didn’t want to manage what the file types were, and this worked fine for me because I just want the user to be prompted to save the file.  I honestly don’t know much about the response object, so don’t count this as the best way to do this.  Essentially, you have to create a byte array and read your string into it using encoding and then write it out using the binarywrite method:

Public Sub OpenDocument(sender As Object, e As System.EventArgs)
     Response.Clear()
     Response.Buffer = True 
     Response.ClearContent()
     Response.ClearHeaders()

     Response.AddHeader("Content-Disposition", "attachment; filename=" &
          sender.commandname.ToString)
     Dim dt As DataTable = getAttachmentFile(sender.commandargument.ToString)
     Response.BinaryWrite(dt.Rows(0).Item("fileobject"))
     Response.Flush()
     Response.Close()

     ‘make sure to clearcontent, hearders, flush, and close or your file will be larger…as large as your
      ‘page is big (it copies your page to the file).  I couldnt’ figure out why my files were larger 
      ‘coming down that going up and this was why.

End Sub

Public Function getAttachmentFile(ByVal attachmentID As Integer) As DataTable
    Dim dt As New DataTable
    Dim db As Database = DatabaseFactory.CreateDatabase()
    Dim dbcommand As DbCommand = db.GetStoredProcCommand("pr_get_attachmentfile")
    Dim reader As RefCountingDataReader = Nothing
    Try
        db.AddInParameter(dbcommand, "@attachmentid", DbType.Int32, attachmentID)
        reader = CType(db.ExecuteReader(dbcommand), RefCountingDataReader)
        dt.Load(reader)
        Return dt
    Catch ex As Exception
        Throw ex
    Finally
        If reader IsNot Nothing Then
            If Not reader.IsClosed Then reader.Close()
        End If
        reader = Nothing
    End Try
End Function

5. Websites

Websites that assisted me in this venture:

http://www.dbtutorials.com/advanced/Save-Img-ToDB-VB.aspx

http://www.codedigest.com/Articles/ASPNET/6_GridView_with_Image.aspx

http://www.kodyaz.com/articles/file-upload-and-save-to-sql-server.aspx

Advertisements

Written by matt

September 28, 2011 at 10:45 pm

Posted in Asp.net, T-Sql

Tagged with

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: