Chapter 2: Common ASP.NET Code Techniques
7. Uploading Files from the Browser to the Web Server via an ASP.NET Page
Whether a user on Monster.com needs to upload her resume, or a GeoCities member wants to upload his personal Web site, there are a vast number of practical scenarios in which the ability for a visitor to upload a file (or files) from his computer to the Web site is essential. With classic ASP, this task usually fell on the shoulders of a third-party component, such as ASPUpload (http://www.aspupload.com/) or SoftArtisan's SA-FileUp (http://www.softartisans.com/). With ASP.NET, however, file upload capabilities are available without the need for any third-party component.
To learn more about file uploading, make sure to check out the "Other Resources" section at the end of this chapter!
When uploading a file from an HTML form, there are a few guidelines you must follow. First, and most importantly, you must specify the multipart/form-data encoding type in the form tag. This encoding type is specified in the form tag in the following syntax:
<form ... enctype="multipart/form-data" ...>
Next, you must provide an INPUT form element that has its TYPE property set to FILE. These types of INPUT form elements are displayed in a user's browser as a text box that contains a Browse button next to it. If the user clicks the Browse button, she will be able to select a file from her hard drive. When she does, the filename will appear in the associated text box. The following HTML would generate such a file upload text box with an accompanying Browse button:
<form enctype="multipart/form-data" method="post"> <input type="file" name="fupUpload"> </form>
Because the previous snippet of code is simply HTML, you could easily add such code to a classic ASP page. The problem was actually retrieving the contents of the uploaded file and saving it to the Web server. Although this could be done with script alone, the implementation was usually difficult and messy enough to warrant the use of a third-party component. (For information on a script-only upload scenario with classic ASP, be sure to read http://www.asp101.com/articles/jacob/scriptupload.asp.)
The complete documentation for uploading files through an HTML form can be found in RFC 1867, "Form-based File Upload in HTML," at http://www.ietf.org/rfc/rfc1867.txt.
With ASP.NET, an entire class exists to handle the uploaded file and its contents. This class, HttpPostedFile, enables a developer to either programmatically save the uploaded file on the Web server's file system or to access the contents of the uploaded file via a Stream. In this section we'll look at two examples: The first example demonstrates how to provide a form to allow the user to upload a file and have it saved on the Web server; the second example illustrates an ASP.NET page that accepts an uploaded image and reports the image's properties. (When examining the second example, note that the image is never saved to the Web server's file system.)
Saving an Uploaded File to the Web Server's File System
When viewed through a browser, Listing 2.7.1 presents the user with a text box and Browse button, enabling the user to select a file from her hard drive that she wanted to upload to the Web server. If the user attempts to upload a file that does not exist, a warning message will be displayed. If, however, the user enters a valid filename (either by typing it in the text box or selecting the file via the Browse button) and the file is successfully saved on the Web server, a confirmation message will be displayed, and the user will be presented with a link to view the uploaded content through the Web server. (Refer to Figure 2.24 to see a screenshot of the user's browser after successfully uploading a file.)
Listing 2.7.1 Component-less File Upload Is Possible via an ASP.NET Page
1: <%@ Import Namespace="System.IO" %> 2: <script language="VB" runat="server"> 3: Sub btnSubmit(sender as Object, e as EventArgs) 4: 'Has the file been uploaded properly? 5: If Not fupUpload.PostedFile Is Nothing Then 6: 'Save the file if it has a filename and exists... 7: If fupUpload.PostedFile.FileName.Trim().Length > 0 AND _ 8: fupUpload.PostedFile.ContentLength > 0 then 9: 10: Const strBaseDir as String = "C:\My Projects\Uploaded Files\" 11: Dim strFileName as String = _ 12: Path.GetFileName(fupUpload.PostedFile.FileName) 13: 14: fupUpload.PostedFile.SaveAs(strBaseDir & strFileName) 15: 16: 'File has been saved! 17: lblResults.Text = "<hr><p>Your file, " & fupUpload.PostedFile.FileName & _ 18: ", has been saved to the Web server!<br>" & _ 19: "[<a href=""/Upload/" & strFileName & """> View File</a>]" 20: End If 21: Else 22: lblResults.Text = "<hr><p>Enter a filename to upload!" 23: End If 24: End Sub 25: </script> 26: 27: <html> 28: <body> 29: <form runat="server" EncType="multipart/form-data"> 30: <h1>File Upload</h1> 31: <b>Select the file to upload:</b><br> 32: <input runat="server" id="fupUpload" type="file" > 33: <p> 34: <asp:button id="btnSubmit" runat="server" Text="Upload File" _OnClick="btnSubmit_Click"/> 35: <p><asp:label runat="server" id="lblResults" /> 36: </form> 37: </body> 38: </html>
Listing 2.7.1 begins by Importing the System.IO namespace. This namespace is imported because, on line 12, the GetFileName method of the Path class is used to snip out the filename of the uploaded file. No namespaces need to be imported to use those classes that assist with browser-based file uploads.
We have a btnSubmit_Click event handler in this ASP.NET page that runs when the btnSubmit button has been clicked. Before we discuss this event handler, let's first examine the HTML content from lines 27 through 38. This content begins with form tag that has both the runat="server" attribute set as well as the enctype="multipart/form-data" setting (line 29). It is vitally important that this enctype parameter be included in the form tag, or else the file upload will not work.
Next, on line 32, a file upload HTML control is created with the id fupUpload. Note that its type property is set to file; again, it is essential that this INPUT tag have its type property set to file for the file upload to work properly. It is important to set the runat="server" property of the INPUT tag so that you can programmatically access the uploaded file. On line 34 a button control, btnSubmit, exists. When this is clicked, the form will be submitted. Finally, on line 35, a label control, lblResults, is created, which will display a success message after the file is uploaded from the client's machine to the Web server.
The btnSubmit_Click event handler begins on line 3 and immediately checks to ensure that the PostedFile property of the fupUpload file upload control is assigned a value. PostedFile is a property of the HtmlInputFile class and represents the file uploaded from the client to the Web server.
This PostedFile property is an instance of the HttpPostedFile class. The HttpPostedFile class has four very useful properties:
ContentLengthRepresents the size, in bytes, of the uploaded file.
ContentTypeRepresents the MIME content type of the uploaded file.
FileNameRepresents the full path and filename on the client's computer of the uploaded file.
InputStreamProvides a Stream instance for access to the uploaded file's bits.
If the form has been posted back and the PostedFile property is not equal to Nothing, the code between lines 6 and 20 will execute. If either of these conditions is False, line 22 will execute, displaying a message to the user to enter a filename to upload.
Line 7 performs one more check on the validity of the file selected by the user for uploading. It checks to ensure that the user has not typed in a filename that does not correspond to a file on his hard drive (by checking that the ContentLength property of the PostedFile property is greater than zero). Furthermore, line 7 checks to ensure that the user did not enter a blank filename in the file upload text box. Assuming that these conditions are met, the code from lines 10 through 19 is executed. This is the code that performs the actual work of saving the uploaded file to the Web server's file system.
On line 10 the constant strBaseDir is assigned the directory to save the uploaded files in. Next, on lines 11 and 12, the filename portion of the uploaded file is snipped out from the FileName property of the PostedFile property and assigned to the variable strFileName. If the user selected to upload the file C:\Resume\MyResume.doc, the FileName property would be equal to C:\Resume\MyResume.doc. Because we already know what directory in which we want to save the file, we simply want just the name of the file (without the path). The System.IO namespace contains a Path class that contains a number of static methods to extract various bits of information from a file or directory path. One of these methods, GetFileName, can be used to quickly snare this portion of the FileName property (line 12).
Next, on line 14, the uploaded file is saved to the Web server's file system. The SaveAs method of the HttpPostedFile class expects a single parameter: the physical path to upload the file to (directory name and file name). Finally, we want to display a message to the user letting him know that his file has been successfully uploaded. Lines 17 through 19 accomplish this task, providing a link to the uploaded file.
If you want the uploaded files to be Web accessible, you must upload them to a Web accessible directory. For this example, I created a virtual directory named /Upload that pointed to C:\My Projects\Uploaded Files\.
Working Directly with an Uploaded File
Listing 2.7.1 illustrated how to upload a file from the client to the Web server and then save this file to the Web server's file system. This is useful for scenarios in which you want the user and, perhaps, other visitors to be able to access the uploaded content at a later date. But what if you only needed to perform some task with the uploaded data and then no longer needed it? Why bother saving it to the file system at all?
The InputStream property of the HttpPostedFile class provides direct access to the contents of the uploaded file in the form of a Stream. Therefore, a developer can programmatically access this information as opposed to saving the file first, if he so desires.
Listing 2.7.2 illustrates accessing an uploaded file via the InputStream. Listing 2.7.2 allows the user to upload an image file and then displays information about the image, such as its dimensions, resolution, and file type.
Listing 2.7.2 Developers Can Programmatically Access the Uploaded File's Contents
1: <%@ Import Namespace="System.IO" %> 2: <%@ Import Namespace="System.Drawing" %> 3: <%@ Import Namespace="System.Drawing.Imaging" %> 4: <script language="VB" runat="server"> 5: Sub btnSubmit_OnClick(sender as Object, e as EventArgs) 6: 'Read in the uploaded file into a Stream 7: Dim objStream as Stream = fupUpload.PostedFile.InputStream 8: Dim objImage as System.Drawing.Image = _ 9: System.Drawing.Image.FromStream(objStream) 10: 11: 'List the properties of the Image 12: Dim strFileName as String = fupUpload.PostedFile.FileName 13: lblImageProps.Text = "<hr><p><b>Uploaded File:</b> " & strFileName & _ 14: "<br><b>Type:</b> " 15: 16: 'Determine the file type 17: If objImage.RawFormat.Equals(ImageFormat.Gif) then lblImageProps.Text &= "GIF" 18: If objImage.RawFormat.Equals(ImageFormat.Bmp) then lblImageProps.Text &= "BMP" 19: If objImage.RawFormat.Equals(ImageFormat.Jpeg) then lblImageProps.Text &= "JPEG" 20: If objImage.RawFormat.Equals(ImageFormat.Icon) then lblImageProps.Text &= "Icon" 21: If objImage.RawFormat.Equals(ImageFormat.Tiff) then lblImageProps.Text &= "TIFF" 22: 23: lblImageProps.Text &= "<br><b>Dimensions:</b> " & objImage.Width & _ 24: " x " & objImage.Height & " pixels<br><b>Size:</b> " & _ 25: objStream.Length & " bytes<br>" & "<b>Horizontal Resolution:</b> " & _ 26: objImage.HorizontalResolution & _ 27: " pixels per inch<br><b>Vertical Resolution:</b> " & _ 28: objImage.VerticalResolution & " pixels per inch" & _ 29: "<p><img src=""" & strFileName & """>" 30: 31: objImage.Dispose() 32: End Sub 33: </script> 34: 35: <html> 36: <body> 37: <form runat="server" EncType="multipart/form-data"> 38: <h1>Image Converter</h1> 39: <b>Select an image file:</b><br> 40: <input runat="server" id="fupUpload" type="file"> 41: <p> 42: <asp:button id="btnSubmit" runat="server" Text="Get Image Info" 43: OnClick="btnSubmit_OnClick" /> 44: 45: <p><asp:label id="lblImageProps" runat="server" /> 46: </form> 47: </body> 48: </html>
Listing 2.7.2 begins, as many of our examples have thus far, with a number of Import directives. Because we will be dealing with the Stream class, the System.IO namespace is imported (line 1). Likewise, because Listing 2.7.2 will need to be able to determine an image's various properties, we will need to work with the Image and ImageFormat classes, which require the System.Drawing and System.Drawing.Imaging namespaces, respectively.
Because Listing 2.7.2 does not contain the Page_Load event handler, let's begin our examination of the code starting with the HTML content (lines 35 through 48). (This HTML content is very similar to the HTML content presented in the earlier listing.) We start by creating a postback form that contains the always important enctype="multipart/form-data" property (line 37). Next, a file upload HTML control is created on line 40. A button control, btnSubmit, is created on lines 42 and 43; note that the button's OnClick event is wired up to the btnSubmit_OnClick event handler, which we'll examine shortly. Finally, on line 45, there is a label control that will be used to display the uploaded image file's properties.
The HTML content defined from line 35 through line 48 will present the user with an upload form field (a text box with the accompanying Browse button). After the user selects a file and clicks the Get Image Info button, the form will be submitted, the selected image file will be uploaded to the server, and the btnSubmit_OnClick event handler will fire, displaying the properties of the uploaded image file.
The btnSubmit_OnClick event handler begins on line 5 and starts by assigning a Stream variable, objStream, to the Stream instance returned by the InputStream property (line 7). At this point, the contents of the uploaded file are accessible via the objStream variable. Next, on line 8, an Image class variable is created and assigned the value returned from the FromStream method of the Image class. (Recall that the Image class is abstract, and therefore cannot be instantiated with the new keyword.)
Now that we have an Image class instance representing the uploaded image file, we can go ahead and list its properties. On line 12 we assign the FileName property of the PostedFile property to the variable strFileName. Next, this variable is displayed in the lblImageProps label control (lines 13 and 14).
Next, on lines 16 through 21, we check to determine the file type of the uploaded image. The RawFormat property of the Image class returns an instance of the ImageFormat class that represents the file format of the image. The Equals method is a method of the Image class (inherited from the Object class) that checks to determine if two Objects are equivalent. We check the return value of the RawFormat property against the various ImageFormat types to determine the file type of the upload image.
In lines 23 through 29, we output the various properties of the Image class: the Height, Width, file size (Length property of the Stream class), HorizontalResolution, and VerticalResolution. We also output an IMG tag with the SRC property specifying the path to the image on the client's computer. Finally, on line 31, we clean up the resources claimed by the objImage class.
Figure 2.25 shows the output of Listing 2.7.2 after a user has selected an image file from his hard drive. Keep in mind that the uploaded image file is never saved to the Web server's file system.