📜 ⬆️ ⬇️

Implementation of multi-boot files with progress indicators on ASP.NET

In this article, Mark Sullivan tells how using ASP.NET and the Uploadify libraries to implement multi-loading of files with dynamic progress indicators. The Http-processor class created in the process, along with the control class, is suitable for any other ASP.NET application, which makes this code, on the one hand, a useful plug-in-play solution, and on the other, a visual demonstration of working with Http -handler. He also added a number of additional features to the ASP.NET layer, such as filtering by file extension, postback on completion, and session-based security.



general information


With the release of HTML5, there was hope for the possibility of implementing the simultaneous loading of several files without using ActiveX or other plug-ins. But pure HTML5 does not support dynamic indicators during loading, besides it is processed differently in some browsers. I also needed a secure solution that allows you to implement filtering by file extension.

I discovered Uploadify when I was looking for a replacement for the previous solution. This library was originally written as a free Flash plugin, but in 2012, a solution for HTML5 was also presented. The cost of the HTML5 library varies from $ 5 to $ 100 depending on whether you are going to distribute it. In my opinion, this is a low price for such a high-quality product, but, of course, you decide. In any case, I also made an implementation of the Flash version, if it suddenly comes in handy for someone. Perhaps I myself will be able to apply it to support some of my projects in older browsers. Unfortunately, I have not yet met on the Internet any illustrative examples of the implementation of this solution on ASP.NET.
')


I wrapped the options in a C # web control and C # class (for writing directly to the output stream without controls). Classes handle most of the options handled by the library. I also added JavaScript support to filter downloads from the server and client. With regard to security and filtering on the server side, their implementation requires a session state. However, they help prevent unwanted downloads. Other options needed for the project have also been added to C # classes or controls.

Code usage


Introductory notes

Before using the provided code, you should choose between a free Flash library and a paid HTML5 library. All the contents of the first must be placed in the empty subfolder Uploadify, and the second in UploadiFive. The Flash version is used in the third demo (Demo3.aspx), and the HTML5 version is used in the first two.

Alternatively, you can wrap the classes around the HTML5 multiload file field.

Connecting libraries

You need to add links to the jQuery library and the selected Uploadify library. The standard CSS file comes with packages, so I added a link to it in my code. Add the following lines inside the head tag of your ASPX form.

<script src="jquery/jquery-1.10.2.js" type="text/javascript"></script> <script src="uploadifive/jquery.uploadifive.js" type="text/javascript"></script> <link rel="stylesheet" type="text/css" href="uploadifive/uploadifive.css" /> 

or so:

 <script src="jquery/jquery-1.10.2.js" type="text/javascript"></script> <script src="uploadify/jquery.uploadify.js" type="text/javascript"></script> <link rel="stylesheet" type="text/css" href="uploadify/uploadify.css" /> 


Adding to the page control

In ASP.NET web forms, you can add a control to a page by creating an instance of the UploadiFiveControl class code and adding it to the form or placeholder.

 // Get the save path string savepath = Context.Server.MapPath("files"); // Create the upload control and add to the form UploadiFiveControl uploadControl = new UploadiFiveControl { UploadPath = savepath, RemoveCompleted = true, SubmitWhenQueueCompletes = true, AllowedFileExtensions = ".jpg|.bmp" }; form1.Controls.Add(uploadControl); 

Adding a control to MVC is similar.

Working with the Flash version is quite simple, since it uses the same classes and control, but the version flag is set to Flash, not to default. The remaining changes, including the names of some options, are processed by the control.

 // Create the upload control and add to the form UploadiFiveControl uploadControl = new UploadiFiveControl { UploadPath = savepath, RemoveCompleted = true, SubmitWhenQueueCompletes = true, AllowedFileExtensions = ".jpg|.bmp", Version = UploadiFive_Version_Enum.Flash }; form1.Controls.Add(uploadControl); 

Http handler for downloaded files

You also need to add the UploadiFiveFileHandler Http handler class to the project, which processes the files as they are uploaded to the server. This part of the implementation is the least described anywhere in reference books or blogs, and that is why I devoted the entire next section to it. To enable this class, copy the UploadiFiveFileHandler.ashx and UploadiFiveFileHandler.ashx.cs files into your project.

Http Handler and Security

Creating an Http handler is quite simple, but it is much more difficult to ensure its flexibility and to introduce some additional functions. I used session state to secure and transfer information from the loading page to the handler. For this, I created the class UploadiFive_Security_Token. Here’s what his ad looks like:

 /// <summary> Token used to add security, via adding a key to the session /// state, for uploading documents through this system </summary> public class UploadiFive_Security_Token { /// <summary> Path where the uploaded files should go </summary> public readonly string UploadPath; /// <summary> List of file extensions allowed </summary> public readonly string AllowedFileExtensions; /// <summary> Name of the file object to use in your server-side script</summary> public readonly string FileObjName; /// <summary> The GUID for this security token </summary> public readonly Guid ThisGuid; /// <summary> Constructor for a new instance of the UploadiFive_Security_Token class </summary> /// <param name="UploadPath" /> Path where the uploaded files should go /// <param name="AllowedFileExtensions" /> List of file extensions allowed /// <param name="FileObjName" /> Name of file object to use in your server-side script public UploadiFive_Security_Token(string UploadPath, string AllowedFileExtensions, string FileObjName ) { this.UploadPath = UploadPath; this.AllowedFileExtensions = AllowedFileExtensions; this.FileObjName = FileObjName; ThisGuid = Guid.NewGuid(); } } 

This class contains the path to the file, the valid file extensions and the key by which the handler will find the file. When rendering the UploadiFiveControl class, a new security token is created, which is stored in session state under the GUID. The GUID itself is added to the FormData dictionary, then transferred to the Uploadify library, and then returned to the processor with the file data. After that, the handler can find a security token in the session based on the corresponding GUID.

 // Create a new security token with all the configuration info UploadiFive_Security_Token newToken = new UploadiFive_Security_Token(UploadPath, AllowedFileExtensions, FileObjName); // Add this token to the current session for the HttpHandler HttpContext.Current.Session["#UPLOADIFIVE::" + newToken.ThisGuid.ToString()] = newToken; // Save the token into the formdata so comes to the HttpHandler FormData["token"] = newToken.ThisGuid.ToString(); 

Form data is included in the JavaScript code for creating the Uploadify control. The following is an example of the resulting HTML code without any restrictions on the file extension:

 <input id="file_upload" name="file_upload" class="file_upload" type="file" /> <script type="text/javascript"> $(document).ready(function() { $('#file_upload').uploadifive({ 'fileObjName': 'Filedata', 'formData': { 'token' : 'da66e0ad-750b-4d76-a016-72633dea8b53' }, 'onQueueComplete': function (uploads) { $('#file_upload').closest("form").submit(); }, 'uploadScript': 'UploadiFiveFileHandler.ashx' }); }); </script> 

As seen above, the generated security token GUID is included in the JavaScript code to initialize the library.

The Http handler needs access to the session state, so it needs to implement the IReadOnlySessionState interface. Here is the full code of the ProcessRequest handler method without any restrictions on the file extension:

 Context.Response.ContentType = "text/plain"; Context.Response.Expires = -1; // Try to get the security token key string tokenKey = Context.Request["token"]; if (tokenKey == null) { Context.Response.Write("No token provided with this request"); Context.Response.StatusCode = 401; return; } // Try to get the matching token object from the session UploadiFive_Security_Token tokenObj = Context.Session["#UPLOADIFIVE::" + tokenKey] as UploadiFive_Security_Token; if (tokenObj == null) { Context.Response.Write("No matching server-side token found for this request"); Context.Response.StatusCode = 401; return; } try { // Get the posted file from the appropriate file key HttpPostedFile postedFile = Context.Request.Files[ tokenObj.FileObjName ]; if (postedFile != null) { // Get the path from the token and ensure it exists string path = tokenObj.UploadPath; if (!Directory.Exists(path)) Directory.CreateDirectory(path); string filename = Path.GetFileName(postedFile.FileName); postedFile.SaveAs(path + @"\" + filename); // Post a successful status Context.Response.Write(filename); Context.Response.StatusCode = 200; } } catch (Exception ex) { Context.Response.Write("Error: " + ex.Message); Context.Response.StatusCode = 500; } 

If the token in the session is not found, the file will not be allowed to be downloaded.

File Extension Restrictions

Good practice when developing a bootloader is to add a mechanism for restricting files by their extension. In this section, I will show how I managed to do this on the client and server side, taking into account the security parameters discussed above.

Check on the client side is implemented as an event in the UploadiFive library. But the event of adding an onChange file can be used outside the library.

Valid extensions are passed to the UploadiFiveControl control as a string, separated by commas.

 uploadControl.AllowedFileExtensions = ".jpg|.bmp"; 

If everything is set correctly, the JavaScript code is attached to the onAddQueueItem event when rendering HTML.

 <script type="text/javascript"> $(document).ready(function() { $('#file_upload').uploadifive({ 'fileObjName': 'Filedata', 'formData': { 'token' : '4c893799-fd21-4d85-80c4-e32e6cacc794' }, 'removeCompleted': true, 'onAddQueueItem' : function(file) { var extArray = JSON.parse('[ ".jpg", ".bmp" ]'); var fileName = file.name; var ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase(); var isExtValid = false; for(var i = 0; i < extArray.length; i++) { if ( ext == extArray[i] ) { isExtValid = true; break; } } if ( !isExtValid ) { alert("File types of '<extension>' are not allowed".replace('<extension>', ext)); $('#file_upload').uploadifive('cancel', file); } }, 'uploadScript': 'UploadiFiveFileHandler.ashx' }); }); </script> 

This code takes the list of extensions converted by the C # control to a JSON array and compares the specific extension with the array data. If this extension is invalid, the user receives a warning, and the file is removed from the download queue.

The Http handler code on the server side looks about the same. It uses a security token to get a list of valid extensions:

 // Get the filename for the uploaded file string filename = Path.GetFileName(postedFile.FileName); // Are there file extension restrictions? if ( !String.IsNullOrEmpty(tokenObj.AllowedFileExtensions)) { string extension = Path.GetExtension(postedFile.FileName).ToLower(); List<string> allowed = tokenObj.AllowedFileExtensions.Split("|,".ToCharArray()).ToList(); if (!allowed.Contains(extension)) { Context.Response.Write("Invalid extension"); Context.Response.StatusCode = 401; return; } } // Save this file locally postedFile.SaveAs(path + @"\" + filename); 

Now the restriction of invalid formats on the client and server side can be considered implemented.

File Size Limits

There are also a number of ways to limit file downloads to their size. The IIS server has such limitations, and this should be kept in mind when working. In addition, Uploadify also has a way to limit the size of uploaded files, as I mentioned.

In the web.config file, you can set the maximum size for a valid content type, which will impose restrictions on the downloaded files. The requestLimits tag is responsible for this. It accepts values ​​in the number of bytes. For example, the code snippet below limits the size of the uploaded file to approximately 200 MB. This fragment was also included in the code in the web.config file.

 <configuration> <system.webServer> <security> <requestLimits maxAllowedContentLength="209715200" /> </security> </system.webServer> </configuration> 

In addition, the Uploadify library uses the JavaScript fileSizeLimit property as a FileSizeLimit in C # classes. It looks like a string like “100KB” or “200MB”.

Flash as a backup and compatibility issues

Some popular browsers, especially IE9, do not receive the necessary updates in order to support HTML5. Therefore, if in our case you do not take care of the backup option, the user of the old browser will see only the standard HTML file upload field without a download button. In this case, of course, nothing will work.

Fortunately, we can take as a backup version a Flash version, which is very similar to the HTML5 version. To do this, use the onFallback event. It was additionally implemented in C # classes available for download here. If you add this function to the current code using the UploadiFiveControl control, you get something like this:

 // Create the upload control and add to the form UploadiFiveControl uploadControl = new UploadiFiveControl { UploadPath = savepath, RemoveCompleted = true, Version = UploadiFive_Version_Enum.HTML5, RevertToFlashVersion = true, NoHtml5OrFlashMessage = "Please try a more current browser" }; 

In the code below, we tell the class to add a backup Flash option, setting the RevertToFlashVersion property to true. If the browser does not support either HTML5 or Flash, you can display a warning window using the NoHtml5OrFlashMessage property.

The example above generates the following JavaScript code that will be included in the HTML code of your aspx page:

 <script type="text/javascript"> $(document).ready(function() { $('#file_upload').uploadifive({ 'fileObjName': 'Filedata', 'formData': { 'token' : 'f8916a9f-9dda-441f-a58b-13948e61f7e7' }, 'removeCompleted': true, 'uploadScript': 'UploadiFiveFileHandler.ashx', 'onFallback': function() { // Revert to flash version if no HTML5 $('#file_upload').uploadify({ 'fileObjName': 'Filedata', 'formData': { 'token' : 'f8916a9f-9dda-441f-a58b-13948e61f7e7' }, 'removeCompleted': true, 'swf': 'uploadify/uploadify.swf', 'uploader': 'UploadiFiveFileHandler.ashx', 'onFallback': function() { alert('Please try a more current browser'); } }); } }); }); </script> 

Now we have a working backup version for old browsers, which is almost identical to the main version. To trigger the backup mechanism, a Flash version is needed, but only as a secondary option, as in my example. Of course, you will need to download both versions and link the links to the corresponding CSS and JS files inside the head tag.

Link to code:

All code and four aspx demo pages are available for download at the link.

Source: https://habr.com/ru/post/341366/


All Articles