Practical ASP.NET
Getting Files To and From the Server
The FileUpload control and the ASP.NET Response object let you move files between the browser and the server. And moving in each direction just requires a few lines of code.
On occasion, what the user enters into the controls on the page isn't enough -- sometimes the user needs to give you a complete file. The reverse is also true: Sometimes sending the user a page isn't enough: you need to get a whole file down to the user's computer.
Moving Files to the Service
To get a file to the server you need to add an input tag of type file to your page. In ASP.NET 2.0 the FileUpload server-side control does just that:
‹input type="file" name="FileUpload1" id=" FileUpload1" /›
When the user uses the FileUpload control in the browser, the browser opens up a file dialog that lets the user select the file to upload. The file is not, however, uploaded until the user submits the page. At that point, on IIS, the file is uploaded to a quarantine area.
Back on the server, the FileUpload control doesn't fire an event on the server when it's used in the browser -- it has no equivalent to a Button's Click event. Instead, to determine if the user used the control to upload a file, you have to check the control's HasFile property, as this example does:
If Me.FileUpload1.HasFile Then
End If
If a file was uploaded, you'll want to move the file out of the quarantine area. You can use either the control's SaveAs method (which allows you to save the file to physical path on the hard disk) or read the file byte by byte through the control's FileContent property (which is a stream). You can retrieve the name that the file had on the user's disk from the control's FileName property.
This example converts the filename to a physical path in the website using the Server object's MapPath method and the FileName property:
If Me.FileUpload1.HasFile Then
Me.FileUpload1.SaveAs( _
Server.MapPath( System.IO.Path.GetFileName(
Me.FileUpload1.FileName)))
End If
Of course, you want to protect yourself from saving malicious files to your server. You can use the ContentType on the control's PostedFile property to check the MIME type of the file. This code checks that the file being uploaded is a text file:
If Me.FileUpload1.HasFile AndAlso _
Me.FileUpload1.PostedFile.ContentType = "text/plain" Then
Finally, you want to protect yourself from denial of service attacks. These attacks can take two forms: uploading really, really large files or uploading standard sized files really, really slowly. You can cut off both attacks by specifying a maximum file size and upload time in your web.config file with the httpRuntime element.
This example limits files to 1024 kilobytes (one megabyte) in size and uploads to five minutes (300 seconds) in duration:
‹httpruntime maxrequestlength="1024" executiontimeout="300"›‹/httpruntime›
Getting Files to the Client
I suspect that there are probably several ways that you can send files to the client. However, I've been using a mechanism based around the Response object since I started programming way back in ASP.
Fundamentally, instead of returning HTML text to the client, I send some other form of data. As part of sending the data to the client, I tell the browser what kind of data I'm sending and force the browser to open a File Download dialog box.
I first convert the data into a byte array. This example assumes the output is in a string called Exportdata:
Dim abytOutput() As Byte
abytOutput = (New System.Text.UTF8Encoding).GetBytes(ExportData)
Now that I have a byte array, I use the Response object for the page to set the ContentType of what I'm sending to the client. In this example, I set the ContentType to "text/csv" after clearing the buffer:
Response.ClearContent()
Response.ContentType = "text/csv"
My next step is to add a Content-Disposition header to my output. This will cause the browser to display the File Download dialog with a default file name. This example sets the file's default name to "MyFile.csv" after clearing any existing headers:
Response.ClearHeaders()
Response.AddHeader("Content-Disposition",
"attachment;filename=MyFile.csv;")
Finally, I write the byte array to the client using the Response object's OutputStream property. The Write method accepts the byte array, a start point and an end point. Typically, I start in position zero and write all the bytes in the array as in the following example. After sending the content to the client, I flush the output buffer and close the stream:
Response.OutputStream.Write(abytOutput, 0, abytOutput.Length)
Response.Flush()
Response.Close()
With the FileUpload control and the Response object, you can move files in both directions: up to the server and down to the client.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.