Application Security Testing: An Integral Part of DevOps
There are literally thousands of facets to the .NET Framework, and writing them down functions as a way to learn, memorize or work out the details. This is especially useful for things you use infrequently, such as thumbnail images. I wrote an application several years ago to manage label images, but when I sat down to try to upload an image, I discovered that I had forgotten how to create a thumbnail image—a subject that makes a useful article topic.
This article shows you how to use an <input> control to browse and upload images, and then convert those images to thumbnail versions for display on the web site. The code is straightforward without a lot of sanity checking, as is the ASPX, and works fine for example purposes, but for a production application you'll want to add validation and error checking (but that's another article).
Uploading Files with the <input> Element
The <input> HTML element is a versatile HTML control. Based on the value you supply for the element's type attribute, the <input> element can function (and display) as a general-purpose button, a radio, reset, or submit button, a checkbox, a file uploader, a hidden control, an image, a password field, or a text control. For the file uploader, set the <input> element's type attribute to file, and the <input> control displays a textbox and a button labeled "Browse." While you can code this manually, the easiest way to display a file uploader is to select the Input (File) item from the HTML tab of Visual Studio's Toolbox.
Figure 1. Design-Time View: From this design-time view, you can see how the sample page uses the table to lay out the other HTML elements.
The sample ASPX page contains this type of <input> element, as well as a label, a button to submit a selected image file, and an image element. The server creates a thumbnail of that image and returns it to the browser, which displays it in an <img> element on the same page. An HTML table handles the control layout (see Listing 1). At design-time, the page looks like Figure 1.
HTML tables are pretty versatile and easy to use. If you want a simple spreadsheet-like table then use <tr> elements to add rows and <td> elements to add cells. If you want a single cell to span multiple rows then set the <td> element's rowspan attribute, or to span multiple columns, set its colspan attribute.
The only other interesting aspect of the HTML in Listing 1 is that the <img> control (in column 2 in Figure 1) has an ID attribute, and its runat attribute is set to server. Setting the runat attribute lets you manipulate the <img> control in the server-side code behind. You'll use this to display the created thumbnail image.
Saving the Uploaded Image
When an <input> control has its type attribute set to file, a user can click the Browse button to select a file. After selecting a file, when the user submits the page the <input> control will have a populated PostedFile property, which is a class that will contain the file's contents and other file information, such as its file name. To be specific, PostedFile is an instance of the HttpPostedFile class.
To use the GUI, a user clicks the Browse button, selects a file, and then clicks the Upload button. On the server, you can save the posted file by calling the <input> control's PostedFile.SaveAs method, passing the path and file name where you want to save the file. The following code shows just how easy this part of the process is:
Private Const imagePath As String = "~/Images/" Private Sub SaveUploadedFile(ByVal filename As String) File1.PostedFile.SaveAs(MapPath(imagePath) + filename) End Sub
In the preceding code, File1 is the ID of the <input> control. (The <input> control has the runat="server" attribute set, so even though it's an HTML control, it is available on the server side too.)
Creating and Display the Thumbnail
The sample program uses a Button control to submit the default form containing the <input> element. The <input> element has the posted file. The complete code, shown in Listing 2, performs four steps:
- Extract the file name from the posted file path
- Save the uploaded file to a pre-determined server path
- Save a thumbnail copy of the image
- Set the <img> element's src attribute to the thumbnail's location
When the user submits the file, File1.PostedFile.FileName contains the client's local path (the location of the file on the user's machine). You will want to extract the filename without the path, which you can do with the System.IO.Path class's GetFileName method.
The SaveUploadedFile method shown earlier uses the PostedFile.SaveAs method to save the file. Note that it uses the MapPath method, which maps a server-side relative path to its physical path. It appends the file name to that path, which creates a complete pathname where it saves the uploaded file. (You could also save the file to a database or display the file contents on the web page.)
The next step is to create the thumbnail image. The SaveThumbnail method creates a thumbnail copy of the image.
Private Sub SaveThumbnail(ByVal filename As String) Dim image As Image = image.FromStream( _ File1.PostedFile.InputStream) Dim thumbnail As Image = image.GetThumbnailImage( _ 100, 100, AddressOf Callback, IntPtr.Zero) thumbnail.Save(MapPath(GetThumbnailPath(filename))) End Sub Private Function GetThumbnailPath( _ ByVal filename As String) As String Return "~/Images/" + _ String.Format(thumbnailMask, filename) End Function
The SaveThumbnail method creates an in-memory System.Drawing.Image instance from the HttpPostedFile's InputStream property by passing the input stream to the Image.FromStream method. Next, it creates a thumbnail copy of the image by calling the Image's GetThumbnailImage method. GetThumbnailImage has some odd arguments, mostly because its implementation calls into unmanaged code. The first two arguments are the width and height of the thumbnail image, the third argument is an unused callback delegate, which you must provide, even though it's unused. The fourth argument is another unused relic to satisfy the native code call. The final argument must be IntPtr.Zero. (Obviously, a better thumbnail wrapper probably needs to be created.) Finally, the method saves the thumbnail in the same location as the original image, but with the prefix thumbnail_ added to the file name.
Finally, the SetPreviewImage uses the virtual path for the thumbnail and assigns it to the <img> element's src property.
Private Sub SetPreviewImage(ByVal filename As String) PreviewImage.Src = GetThumbnailPath(filename) End Sub
The topic of uploading files is a bit of an oldie, but it is still a goody. Creating and showing thumbnails is a good way to let users preview numerous images, and then select a full-sized version. The GetThumbnailImage method makes a call into unmanaged native code, so it has some unused "junk" arguments that make it a little cumbersome to use—but now you have the CliffNotes.