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)=nullAS
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
ENDCREATE PROCEDURE [dbo].[pr_get_attachmentfile]
@attachmentid int
AS
BEGIN
select FileObject from tblattachment
where AttachmentID = @attachmentidEND
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 />
<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>
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
