Monthly Archives: April 2013

Ajax file upload

Sometimes it would be useful to upload a file to a website while the rest of the page remains unchanged, so with Ajax. I’ve got bad news for you: the Level 1 version of the XMLHttpRequest object doesn’t support that, so if you must support older browsers, you need some tricks.

One of the most widely used tricks is using an iframe, however in that case the results from the server (for example the validation errors) are also delivered to the iframe. So if you work with a hidden iframe and you need the result in client-processable way in JavaScript, you have to dig it from the iframe. Fortunately the jQuery Form Plugin can help you to solve these issues.

First, create a view-model on the server, in which a HttpPostedFileBase property represents the uploaded file. I’ve also added a Name property for demonstration purposes:

public class UploadVM
{
    [Required( ErrorMessage = "Please enter a name!" )]
    public string Name { get; set; }

    [Attachment]
    public HttpPostedFileBase Attachment { get; set; }
}

The [Attachment] is the file validation attributed I presented in an earlier post. It checks whether the file is submitted, and also checks the extension and the size of the file.

For this model you can create a form that submits the file and the provided name:

@using( Html.BeginForm( "Index", "Home", FormMethod.Post, 
        new { id = "myForm", enctype = "multipart/form-data" } ) )
{
    <p>
        <label for="txtName">Name:</label>
        <input type="text" id="txtName" name="Name" />
    </p>

    <p>
        <label for="fupAttachment">File:</label>
        <input type="file" id="fupAttachment" name="Attachment" />
    </p>

    <p>
        <input type="submit" value="Upload" />
    </p>    

    <div id="errors"></div>
}

It’s important that the form must have an enctype attribute with multipart/form-data value, because that supports file upload. I also created a div with errors id, which will host the error messages.

This form posts to the Index action of the HomeController which can be implemented like this:

[HttpPost]
public ActionResult Index( UploadVM model )
{
  if( !this.ModelState.IsValid )
  {
    string firstError = ModelState.First( m => m.Value.Errors.Any() )
.Value.Errors[ 0 ].ErrorMessage;
return this.FileUploadFailure( firstError ); } // Process the file here string message = String.Format( "The file '{0}' is successfully uploaded.",
model.Name );
return this.FileUploadSuccess( message ); }

If there is any validation error in the view-model, the runtime adds them to the ModelState thanks to the attributes. Just as always the ModelState is check in the beginning of this method, and if there is any error an error message is returned, if not, then the file is processed and a success message is returned to the client.

The returned value is a JSON object, because that comes really handy on the client. But don’t forget, that it will land in an iframe, and not every browser tolerates an application/json result in iframes. Fortunately the jQuery Form Plugin supports a simple hack: just wrap the result into a <textarea> element and send it back in a text/html response, the plugin will do the rest.

To simplify (and standardize) this wrapping, you can create a custom result type:

public class FileUploadJsonResult : JsonResult
{
  public override void ExecuteResult( ControllerContext context )
  {
    this.ContentType = "text/html";
    context.HttpContext.Response.Write( "<textarea>" );
    base.ExecuteResult( context );
    context.HttpContext.Response.Write( "</textarea>" );
  }
}

You can also create extension methods for the Controller class to create these upload results just like any other result from the action methods:

public static FileUploadJsonResult FileUploadSuccess( 
this Controller controller, string successMessage = null ) { return new FileUploadJsonResult { Data = new { Success = true, Message = successMessage } }; } public static FileUploadJsonResult FileUploadFailure(
this Controller controller, string errorMessage ) { return new FileUploadJsonResult { Data = new { Success = false, Message = errorMessage } }; }

Here you can create a custom object with any structure in the Data property that will be delivered to the client in JSON. The code above just signals the result of the file upload in the Success property, and returns a detailed status message in the Message property that can be displayed by the browser.

So now you have fully functional server side: a form that POSTs to an action which validates the input, and if every field is valid, processes the values and returns a status in a JSON object.

Let’s implement the client side, of course with the jQuery Form Plugin. As its name implies, this is a jQuery plugin that you can apply to the jQuery object which wraps the form element (the $form in this case):

$form.ajaxForm({
    iframe: true,
    dataType: "json",
    beforeSubmit: function () {
// TODO
}, success: function (result) { // TODO
}, error: function () { // TODO
} });

You usually have to implement the following callback functions:

  • beforeSubmit: this is called before the POST is sent to the server. This is where you can display a “please wait” message, a progress indicator, or simply block the form controls using the jQuery BlockUI plugin.
  • success: this is called when the upload is completed successfully. At least in theory. In my experience this callback is also called when the server returns some low-level error. If there was no error, you receive the response from the server in the function parameter in JSON, but be prepared, that in case of error it can be undefined as well!
  • error: this is called when an error occurred during postback.

Here is an example of a working success callback implementation:

if (!result) {
  $errors.html('<div class="validation-summary-errors"><ul><li>Oooops....
</li></ul></div>'
); } else { $form.resetForm(); if (result.Success === true) { var message = result.Message; if (message && message.length > 0) { $errors.html( message ); } } else { $errors.html('<div class="validation-summary-errors"><ul><li>{0}
</li></ul></div>'
.format(result.Message)); } }

In this code the errors are displayed in a complex HTML fragment. The reason for that is that the same HTML fragment is generated by the standard ASP.NET MVC validation helpers, so with this code our Ajax errors will be rendered with the same style.

The format function acts like the String.Format method on the server and can be implemented like this:

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (m, n) {
    if (m === "{{") { return "{"; }
    if (m === "}}") { return "}"; }
    return args[n];
  });
};

If you return all ModelState errors from the server, you can render them in a loop.

You can download the full source code with tons of comment from the following link: http://sdrv.ms/10QQLSp

 

Technorati-címkék: ,

File upload validation in ASP.NET MVC

One of the beauties of ASP.NET MVC is model validation. Just attach the attributes from the System.ComponentModel.DataAnnotations namespace to the properties of your model or view-model, and you magically have a working validation without messing up your code.

This works perfectly if you have a simple property, like a string or a number, but you won’t find too much help for validating uploaded files in this namespace. From .NET 4.5 there is a FileExtensionAttribute, but usually a more thorough check is requested.

What you always have to check:

  • Is any file uploaded?
  • The extension of the uploaded file is valid?
  • The size of the uploaded file falls within the accepted range?

Sometimes you also have to check:

Let’s create your own validation attribute! Just make sure you derive your custom attribute class from the ValidationAttribute base class and apply it to HttpPostedFileBase properties in the model.

This is a working solution:

[AttributeUsage( AttributeTargets.Property )]
public sealed class AttachmentAttribute : ValidationAttribute
{
  protected override ValidationResult IsValid( object value, 
ValidationContext validationContext ) { HttpPostedFileBase file = value as HttpPostedFileBase; // The file is required. if( file == null ) { return new ValidationResult( "Please upload a file!" ); } // The meximum allowed file size is 10MB. if( file.ContentLength > 10 * 1024 * 1024 ) { return new ValidationResult( "This file is too big!" ); } // Only PDF can be uploaded. string ext = Path.GetExtension( file.FileName ); if( String.IsNullOrEmpty( ext ) || !ext.Equals( ".pdf", StringComparison.OrdinalIgnoreCase ) ) { return new ValidationResult( "This file is not a PDF!" ); } // Everything OK. return ValidationResult.Success; } }

Just add the [Attachment] attribute to the appropriate properties of the model, and your validation errors will land in the ModelState.

If your app allows uploading different files on different pages, you can create an enum for the different file types and pass that enum to the constructor of the AttachmentAttribute. This way you can centralize the file validation logic in your website.

What else do you usually check when you validate the uploaded files?

 

Technorati-címkék: ,,

Windows Store apps need your disk space

One of the beauties of the Windows Store apps is that they can be updated quite seamlessly: almost automatically, without administrative privileges. Developers enjoy this comfort, and no week pass without new updates to some of the apps on my machine.

Of course this convenience comes with some drawback as well, with the need for disk space. Windows Store applications are installed into the C:\Program Files\WindowsApps hidden folder, which by default can be accessed only by the Trusted Installer service. If you grant yourself access to this folder, you can peek into its content and find out what consumes so much disk space (3GB on my box).

You will see something similar there:

metro-apps-versions

If you dive deeper into these folders, you will see the application files: the full source of HTML5+JavaScript apps, and the compiled DLLs for .NET apps.

You can notice that every version of every apps live in its own folder, so they are completely isolated from each other. With this separation you can easily measure how much disk space an application, or one version of an application requires. If you compare that with the numbers in the PC Settings –> General –> View app sizes list, you will see that it only displays the size of the last version of the apps installed only by the current user.

But why are there multiple versions?

One reason is to isolate the users of the computer from each other. As long as two users of the machine install the same version of the same app, the program files will be stored only in a single instance. But when one user updates the app in her own profile, then the OS will store two versions of the same app. With this not only applications, but also the users are completely isolated from each other.

Another reason is, that Windows preserves the versions that came with the OS, so when you create a new user, she will get the baked in versions.

Now, that you know the reasons, you can definitely ask, how can we get rid of the unused versions?

I have to admit, I’m not aware of any official solution for that. If you know any, please don’t hesitate to share that here in a comment. But, according to some sources, Windows cleans up this folder, “when needed”. Anybody knows what that exactly means?

 

Technorati-címkék:

Content Injector for ASP.NET MVC

If you ever created reusable components for ASP.NET MVC, you’ve probably faced with the issue, that a partial view or a HTML helper requires one or more external CSS stylesheets or JavaScript files. If you consider code performance and quality, you always insert your CSS files at the top, and your JS files at the bottom of the page, paying attention to load them exactly once, even if multiple parts of your page require them. However, that’s not easy, because partial views and HTML helpers are rendered autonomously. Although the problem is not new at all, it’s not easy to solve in WebForms, and definitely difficult in MVC.

You may be not surprised that the freshly baked solution is delivered by Peter Blum in the form of the Content Injector for ASP.NET MVC toolkit. Experienced ASP.NET developers probably saw this name several times, because Peter creates reusable ASP.NET WebControls in his one-person company. Among them probably the most well-known is the Peter’s Data Entry Suite (DES), which contains more than 100 webcontrol that simplify input handling and validation. The DES is a very useful set of components, so it’s not a coincidence that it received many positive feedback, excellent rating and professional awards.

So it was not surprising, that a few weeks ago it was Peter, who came up with an idea in the ASPInsiders list, that solves the problem above. Many of us tried and tested the tool, and provided feedback that he quickly applied to the source code, so I can now full heart recommend the Content Injector for ASP.NET MVC toolkit, especially in the NuGet package form.

Using the package is very straightforward. First, you have to mark the places on your Layout.cshtml page, where you want to insert custom content:

@Injector.InjectionPoint("ScriptFiles")

Next, if for example a view requires the jQuery Validate library, then you can insert the corresponding script files directly from the view:

@Injector.ScriptFile("~/Scripts/jquery.validate.min.js"); 
@Injector.ScriptFile("~/Scripts/jquery.validate.unobtrusive.min.js");

Of course this is only the simplest use case, you can specify additional parameters (eg. order), or you can insert stylesheets, scripts, meta tags, hidden fields, script blocks or anything else, thanks to the extensible and configurable (eg. tracing) nature of the product. You can read about all those options in details in the 23-page User’s Guide.

Another interesting thing is that reacting to the first feedback, Peter connected the Content Injector with the Microsoft Web Optimization Framework, so after installing the ContentInjector.WOF NuGet package, you can insert StyleBundle and ScriptBundle as well.

Downloads:

Grab it, try it, and don’t forget to send him feedback or just a quick thank you!

 

Technorati-címkék: ,,

Hidden flight simulator in Windows 8

Update: Works on Windows 7 too, if Internet Explorer 10 is installed!

If you work with Microsoft products for a while you may remember that Excel 97 contained a very special feature: the flight simulator. This was a hidden feature, a so called Easter egg, which could be activated only with a series of certain steps, but after that you could enjoy a real-time simulation of a 3D world, which was super cool 15 years ago:

excel-97-flight-simulator

According to the Easter eggs is Microsoft products article in Wikipedia, Microsoft formally stopped including Easter eggs in its programs as part of its Trustworthy Computing initiative in 2002.

In contrast to that, thanks to the JavaScript Team, Windows 8 contains (at least) one, actually the reincarnation of the flight simulator. According to urban legends, it was originally a proof of concept demo application which showed the capabilities of the Chakra JavaScript engine, so that it can render a so complex 3D space in real-time (compare that with Doom for Chrome which required the Native Client).

Another nice feature, beside the performance, that this app shows the deep integration of sensors in the platform. If you play this game in a tablet with built-in sensors, you can control your plane by simply moving and rotating your device. That’s a wonderful experience in 3D, even if you already played a race car game in a tablet.

Because this Easter egg is buried deep in the core of the system, you cannot activate it with a magic key combination, instead you have to write some code to start the game. Copy this code into a text file named flight.js and save it to your desktop:

var r = "";
var e = "536F7272792C207468657265206973206E6F204561737465722065676720696E2"+
        "04D6963726F736F66742070726F64756374732E0D0A546869732061727469636C"+
        "6520776173207772697474656E206F6E203120417072696C2032303133203A290"+
        "D0A687474703A2F2F67796F72677962616C617373792E776F726470726573732E636F6D";
for ( var i = 0; i < e.length; ) {
  var l = e.charAt( i ) + e.charAt( i + 1 );
  r += String.fromCharCode( parseInt( l, 16 ) );
  i += 2;
}
WScript.Echo(r);

Then open a Command Prompt and start the script:

wscript flight.js

This is final result:

flight-simulator-tablet

My best score is 10413 and yours?

 

Technorati-címkék: ,