One week ago Digitarald released the version 2.0 beta 4 of his very good FancyUpload component. Unfortunately I’ve noticed that many ASP and ASP.NET users were falling in trouble to implement it. In this tutorial I’ll try to cover the key things to get it working. I’ll be also using this guide to introduce new comers to ASP Xtreme Evolution development process.

Tutorial

Notes

First of all, we need to know how we want users to access the Upload form. In the ASP Xtreme Evolution Framework, it’s usually done by typing something like: http://www.yourdomain.com/fancyupload/ or http://www.yourdomain.com/fancyupload/form/ from where it’s clear that we need to create a fancyupload controller in the controllers folder and inside it have a select case named defaultAction for the first case or form for the second one. I’ll be using the first way for the rest of this tutorial.

Ok, time to focus on the fancyupload view. Although the framework comes with some useful things like the YUI reset-fonts-2.3.1.css and the XML-XSLT behavior, it doesn’t mean that you have to use them. Instead it’s mature enough to let you use any library you want to and even change the behavior to fit your usual MVC approach. For this example we will be using Blueprint as the CSS Framework, Mootools as the Javascript Framework and the Zend Framework structure for the views.

Cooking

After installing the framework in yourdomain.com, open your browser and type www.yourdomain.com/fancyupload/. An error screen should appear telling: Controller ‘fancyupload’ is not available at ‘/app/controllers/fancyupload.asp’ You see it? The framework talks to you! Read the messages carefully, a lot of time can be saved if you have a good relationship with the error messages ;D

This error message is telling that you have to create a file called fancyupload.asp @ /app/controllers/. So go there and create a file with the following ASP Xtreme Evolution Base Controller Content:

<!--#include virtual="/app/core/base.asp"-->
<!--#include virtual="/app/core/lib/framework.class.asp"-->
<!--#include virtual="/app/core/singletons.initialize.asp"-->
<%
select case Session("action")
    case "defaultAction"
        dim sHtml
        Session("this").add "Output.xml", "There isn't a xml layer."
        Session("this").add "Output.xslt", "No xslt layer either."
        Session("this").add "Output.value" , sHtml
    case else
        Core.addError("Action '" & Session("action") & "' is not available at controller '" & Session("controller") & "'")
end select
%>
<!--#include virtual="/app/core/singletons.finalize.asp"-->

In order to exercise, let’s change the view structure to the suggested by the Zend Framework team and assign some variables to be passed to it:

' Commented lines are already there
    ' case "defaultAction"
        ' dim sHtml
        Session("view") = "fancyupload/defaultView"
        Session("this").add "title", "ASP Xtreme Evolution meets FancyUpload"
        Session("this").add "h1", "ASP Xtreme Evolution meets FancyUpload"
        sHtml = Core.computeView()
        ' Session("this").add "Output.xml", "There isn't a xml layer."
        ' Session("this").add "Output.xslt", "No xslt layer either."
        ' Session("this").add "Output.value" , sHtml

Let’s build the view. The ASP Xtreme Evolution Base View Content is:

<!--#include virtual="/app/core/base.asp"-->
<!--#include virtual="/app/core/lib/framework.class.asp"-->
<!--#include virtual="/app/core/singletons.initialize.asp"-->
<!--#include virtual="/app/core/shuttle.unload.initialize.asp"-->
<!--// Content start //-->
<!--// Content end //-->
<!--#include virtual="/app/core/shuttle.unload.finalize.asp"-->
<!--#include virtual="/app/core/singletons.finalize.asp"-->

For html pages we just drop our code between content start and content end, passed variables can be accessed with Session(“this”).item(“variable”) as you can see below:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xml:lang="en">
    <head>
        <title><%= Session("this").item("title") %></title>
        <meta name="content-type" content="charset=utf-8" />
        <link rel="stylesheet" href="/assets/blueprint/screen.css" type="text/css" media="screen, projection" />
        <link rel="stylesheet" href="/assets/blueprint/print.css" type="text/css" media="print" />
        <!--[if IE]><link rel="stylesheet" href="css/blueprint/ie.css" type="text/css" media="screen, projection" /><![endif]-->
        <link rel="stylesheet" type="text/css" media="screen" href="/assets/fancyupload/css/fancyupload.css" />
        <link rel="stylesheet" type="text/css" media="screen" href="/assets/roar/css/roar.css" />
        <style type="text/css">
        /* <![CDATA[ */
        .hide { display: none; }
        div#container-ft {
            margin-top: 20px;
            text-align: right;
        }
        div#container-ft li {
            display: inline;
        }
        /* ]]> */
        </style>
        <script type="text/javascript" src="/assets/fancyupload/js/mootools-trunk-1547.js"></script>
        <script type="text/javascript" src="/assets/fancyupload/js/Swiff.Uploader.js"></script>
        <script type="text/javascript" src="/assets/fancyupload/js/FancyUpload2.js"></script>
        <script type="text/javascript" src="/assets/fancyupload/js/Fx.ProgressBar.js"></script>
        <script type="text/javascript" src="/assets/roar/js/roar.js"></script>
        <script type="text/javascript">
        // <![CDATA[
        window.addEvent("domready", function() {
            var roar = new Roar();
            var swiffy = new FancyUpload2($("fancyupload-status"), $("fancyupload-list"), {
                "url": $("fancyupload-form").action,
                "fieldName": "photoupload",
                "path": "/assets/fancyupload/js/Swiff.Uploader.swf",
                "onLoad": function() {
                    $("fancyupload-status").removeClass("hide");
                    $("fancyupload-fieldset").destroy();
                }
            });
            /**
             * Various interactions
             */
            $("fancyupload-browse-all").addEvent("click", function() {
                roar.alert("System alert", "In order to show the Image class in this example, Browse all feature has been deactivated.");
                // swiffy.browse();
                return false;
            });
            $("fancyupload-browse-images").addEvent("click", function() {
                swiffy.browse({"Images (*.jpg, *.jpeg, *.gif, *.png)": "*.jpg; *.jpeg; *.gif; *.png"});
                return false;
            });
            $("fancyupload-clear").addEvent("click", function() {
                swiffy.removeFile();
                return false;
            });
            $("fancyupload-upload").addEvent("click", function() {
                swiffy.upload();
                return false;
            });
        });
        // ]]>
        </script>
    </head>
    <body>
        <div id="container" class="container">
            <div id="container-hd">
                <h1><%= Session("this").item("h1") %></h1>
            </div>
            <div id="container-bd">
                <form id="fancyupload-form" method="post" action="/fancyupload/upload/" enctype="multipart/form-data">
                    <fieldset id="fancyupload-fieldset">
                        <legend>File Upload</legend>
                        <p>
                            Selected your photo to upload.<br />
                            <strong>This fieldset is just an example fallback for the unobtrusive behaviour of FancyUpload.</strong>
                        </p>
                        <label for="fancyupload-file">Upload Photos:</label><input id="fancyupload-file" name="photoupload" type="file" />
                        <input type="submit" value="Go!" />
                    </fieldset>
                    <div id="fancyupload-status" class="hide">
                        <p>
                            <a href="#" id="fancyupload-browse-all">Browse Files</a> | 
                            <a href="#" id="fancyupload-browse-images">Browse Only Images</a> | 
                            <a href="#" id="fancyupload-clear">Clear List</a> | 
                            <a href="#" id="fancyupload-upload">Upload</a>
                        </p>
                        <div>
                            <strong class="overall-title">Overall progress</strong><br />
                            <img src="/assets/fancyupload/img/progress-bar/bar.gif" alt="Overall Progress" class="progress overall-progress" />
                        </div>
                        <div>
                            <strong class="current-title">File Progress</strong><br />
                            <img src="/assets/fancyupload/img/progress-bar/bar.gif" alt="File Progress" class="progress current-progress" />
                        </div>
                        <div class="current-text"></div>
                    </div>
                    <ul id="fancyupload-list"><li /></ul>
                </form>
            </div>
            <div id="container-ft">
                <hr />
                <ul>
                    <li><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml11" alt="Valid XHTML 1.1" height="31" width="88" /></a></li>
                    <li><a href="http://www.w3.org/WAI/WCAG1A-Conformance" title="Explanation of Level A Conformance"><img height="32" width="88" src="http://www.w3.org/WAI/wcag1A" alt="Level A conformance icon, W3C-WAI Web Content Accessibility Guidelines 1.0" /></a></li>
                </ul>
            </div>
        </div>
    </body>
</html>

Note that the form enctype is multipart/form-data and action is /fancyupload/upload/ which means we have to build now a case “upload” in our fancyupload controller:

' Don't forget to add the required libraries in the top includes
<!--#include virtual="/app/core/base.asp"-->
<!--#include virtual="/app/core/lib/framework.class.asp"-->
<!--#include virtual="/app/core/lib/image.class.asp"-->
<!--#include virtual="/app/core/lib/upload.class.asp"-->
<!--#include virtual="/app/core/singletons.initialize.asp"-->
    case "upload"
        dim FancyUpload, File, Img
        ' Create an upload folder if it doesn't exist
        dim Fso : set Fso = Server.createObject("Scripting.FileSystemObject")
        if( not Fso.folderExists( Server.mappath("uploaded") ) ) then
            Fso.createFolder( Server.mappath("uploaded") )
        end if
        set Fso = nothing
        ' Saving the file
        set FancyUpload = new Upload
        for each File in FancyUpload.Files
            set File = FancyUpload.Files(File)
            if(File.saveToFile(Server.mappath("uploaded/" & File.name))) then
                set Img = new Image
                Img.imagerUri = "http://framework.lojcomm.com.br/cgi-bin/Imager.dll"
                Img.image = Server.mappath("uploaded/" & File.name)
                Img.ProcessBinary = false
                call Img.go()
                sHtml = "{""result"":""success"", ""size"":""" & File.name & " ( " & Img.originalWidth & " x " & Img.originalHeight & ", " & round( File.size / 1024, 1 ) & " kB ) has been saved...""}"
                set Img = nothing
            else
                sHtml = "{""result"":""failed"", ""error"":""Unable to save it...""}"
            end if
            set File = nothing
        next
        set FancyUpload = nothing
        Session("this").add "Output.xml", "There isn't a xml layer."
        Session("this").add "Output.xslt", "No xslt layer either."
        Session("this").add "Output.value" , sHtml
        ' FancyUpload waits for a JSON answer
        Response.contentType = "application/json"

Since we know that the files are images — I’ve disabled the Browse all feature in the HTML —, there’s also an instance of the Image class to get the image width and height. That’s it! The only thing missing now is to put the Blueprint, Mootools, FancyUpload and Roar files in their places. Job done, enjoy your incredible Web 2.0 upload system!

Working example

You can find my working example here and the files here. Don’t forget to download also the AXE (ASP Xtreme Evolution) files