Click here to Skip to main content
Licence CPOL
First Posted 25 Jan 2012
Views 12,280
Downloads 514
Bookmarked 38 times

Asynchronous File Upload

By Ali Al Omairi(Abu AlHassan) | 9 Feb 2012
An Ajax control that enables a user to upload a file asynchronously with extra data
   4.88 (7 votes)

1

2

3
1 vote, 14.3%
4
6 votes, 85.7%
5
4.88/5 - 7 votes
μ 4.88, σa 0.98 [?]
 

Introduction

In many web applications, you need to upload some files or context to the application server. This goal can be achieved by using the control FileUpload which is rendered as <input type="file" />. Unfortunately, FileUpload is not supported in asynchronous post-back which doesn't make sense because the whole web site uses partial page postback except these pages that use FileUpload.

One solution was presented by Sunasara Imdadhusen[^] in Lightweight Image Upload with Animation[^] to post the file in another form. In this article, I am representing the same control with some additions to meet the common requirements of web applications.

Using the Code

First, we convert the control from a chunk of JavaScript code to an extender control. So to create an instance of the extender, we need to register the namespace MyControls.Web which contains the control, add a ScriptManager tag, then we add the extender tag like the following:

<%@ Register Assembly="MyControls.Web" Namespace="MyControls.Web" TagPrefix="web" %>
..
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <web:AjaxAploadExtender ID="AjaxAploadExtender1" runat="server">
        </web:AjaxAploadExtender>
..

The next step is to add properties to the extender which will be used in rendering the settings of the control. These properties are:

  • TargetControlID: holds the id of the control which opens the browser's dialog.
  • AutoPostBack: determines whether the selected file should be posted once the dialog is closed (the default is false).
  • PostBackUrl: holds the URL to the page on which the postback should be processed.
  • SubmitButtonID: holds the id of the button which submits the form (helpful when AutoPostBack is set to false).
  • ResponseType: determines the type of data to be returned back from the server (only useful when you are using json data as a response).
  • OnChangeFunction: holds the name of the function to be called once the user selects a file.
  • OnSubmitFunction: holds the name of the function to be called before form is submitted (You can return false to cancel submission)
  • OnSubmitCompleteFunction: holds the name of the function to be called when form submission is completed.
  • OnResetFunction: holds the name of the function to be called to reset the displaying form.
  • ResetButtonID: holds the id of the button which resets the displaying form.
  • ClearButtonID: holds the id of the button which the file inputs.
  • CauseValidation: determines whether the form should be validated before submission.
  • ValidationGroup: holds the name of the validation group of the displaying form (e.g. "*.jpg;*.bmp;*.gif;*.png|Supported Images Types (*.jpg;*.bmp;*.gif;*.png)|*.*|All Files" see FileDialog.Filter Property[^]).
  • Filter: holds the file filter string of the supported file types.
  • OnUnspportedExtention: holds the name of the function to be called when an unsupported file type is chosen.
<script type="text/javascript">
function OnChangeFunction(file, ext, desc) { ... }
function OnSubmitFunction(file, ext, desc) { ... }
function OnSubmitCompleteFunction(file, response) { ... }
function OnUnsupportedFunction(ext) { ... }
</script>
<Button ID="cmdBrowse" runat="server" Text="Browse" />
<web:AjaxAploadExtender ID="AjaxAploadExtender1" runat="server"
 TargetControlID="cmdBrowse"
 AutoPostBack="true"
 PostBackUrl="~/CrossPostBack/saveupload.aspx"
 OnChangeFunction="OnChangeFunction"
 OnSubmitFunction="OnSubmitFunction"
 OnSubmitCompleteFunction="OnSubmitCompleteFunction"
 Filter="*.jpg;*.bmp;*.gif;*.png|Supported Images Types (*.jpg;*.bmp;*.gif;*.png)"
 OnUnsupportedFunction="OnUnsupportedFunction" >
</web:AjaxAploadExtender>    

As the control uses another form to post-back the file, it uses an iframe to capture the response. The control provides two properties to control the response direction.

  • SubmissionFrameType: which can bo set to one of two values AutoGenerated and UserDefined to determine whether to generate an iframe on submit or not.
  • GetSubmissionFrameFunction: holds the name of the function returning the user defined iframe (when SubmissionFrameType is set to UserDefined).

With addition to pre-submission preview ability on an iframe that has the attribute runat="server".

  • AutoPreview: determines whether the selected file should be previewed once the dialog is closed (the default is false).
  • PreviewFrameID: holds the id of the iframe on which the image should be previewed.
  • OnPreviewFunction: holds the name of the function to be called before the preview (You can return false to cancel the preview).
  • OnPreviewCompleteFunction: holds the name of the function to be called when preview is completed.
<script type="text/javascript" >
function OnPreviewFunction(file, ext, desc) { ... }
function OnPreviewCompleteFunction(file, response) { ... }
</script>
<iframe ID="PrviewFrame" runat="server" ></iframe>
<web:AjaxAploadExtender ID="AjaxAploadExtender1" runat="server"
 AutoPreview="true"
 PrviewFrameID="PrviewFrame"
 OnPreviewFunction="OnPreviewFunction"
 OnPreviewCompleteFunction="OnPreviewCompleteFunction" >
</web:AjaxAploadExtender>    

Perhaps the most important thing that should be added to this control is a way to add extra data to the form to be posted. Well, this was presented in version in Imdadhusen's article, but, in my opinion, it wasn't simple. So I added an inner property to the control to hold these DataEntrys as pairs of Name(represent the name of the hidden input) and EvaluateKey(which is used by EvaluateFunction to evaluate the value of the hidden input to be post).

<script type="text/javascript" >
function AjaxApload1_Evaluate(key) { ... }
</script>
<web:AjaxAploadExtender ID="AjaxAploadExtender1" runat="server"
 EvaluateFunctionName="AjaxApload1_Evaluate" >
 <ExtraData>
     <web:DataEntry Name="Title" EvaluateKey="Title" />
     <web:DataEntry Name="Description" EvaluateKey="Description" />
 </ExtraData>
</web:AjaxAploadExtender>   

Our control also has regular FileUplod properties:

  • FileBytes: Gets an array of the bytes in the uploaded file.
  • FileContent: Gets a Stream object that points to uploaded file.
  • FileName: Gets the name of uploaded file.
  • HasFile: Gets a boolean value indicating whether the extender contains a file.
  • PostedFile: Gets the underlying HttpPostedFile object for a file that is uploaded by using the extender.
  • and also SaveAs() function that saves the contents of an uploaded file to a specified path on the Web server.
protected void AjaxAploadExtender1_Submit(object sender, EventArgs e)
{
    string FirstName = AjaxAploadExtender1.GetValue("first");
    string Inits = AjaxAploadExtender1.GetValue("inits");
    string LastName = AjaxAploadExtender1.GetValue("last");
    int DepartmentID = int.Parse(AjaxAploadExtender1.GetValue("dept"));

    if (AjaxAploadExtender1.HasFile)
    {
        HttpPostedFile userPostedFile = AjaxAploadExtender1.PostedFile;
        string filename = AjaxAploadExtender1.FileName;
        userPostedFile.SaveAs(Server.MapPath("~/UploadedImages/") + filename);
    }
        System.Threading.Thread.Sleep(10000);
}

As we can see in the above block, we can get the ExtraData values simply by calling the function GetValue() the Name of the DataEntry as an argument.

This control has two server-side events Submit which raised when the submission form is submitted, and Preview which is raised when an image file is selected (when AutoPreviw is set to true).

Points of Interest

As we know, to make a regular postback, we need two hidden inputs to be submitted with the form. One is __EVENTTARGET which is to hold the unique id of the control caused the post-back, the other is __EVENTARGUMENT which is to hold the arguments of the post-back event. Like in the function GetData() which is used to get the submission inputs values used in JQuery.ajax() call:

function RemoveImage(img) {
    var counter = img.id.replace('close', '');
    $("#loading").show();
    $.ajax({
        type: "POST",
        url: "CrossPostBack/removeupload.aspx",
        data: GetData(img.getAttribute('title')),
        success: function (msg) {
            $('#current' + counter).fadeOut('slow', function () {
                $("#loading").hide();
                $("#message").show();
                $("#message").html("Removed successfully!");
                $("#message").fadeOut(3000);
            });
        }
    });
};

function GetData(file) {
    return {
    __EVENTTARGET : '<%= this.UniqueID %>',
    __EVENTARGUMENT : 'R',
    __PREVIOUSPAGE : $get('__PREVIOUSPAGE').value,

    filename: file
    };
}

As you can see, the hidden input __PREVIOUSPAGE is used in cross-page postbacking, actually it contains an encrypted version of the current page virtual path. We can add one by calling the method ClientScriptManager.GetPostBackEventReference() with an argument of type PostBackOptions with action URL not empty. In my control, I called the method ClientScriptManager.GetPostBackEventReference() and just did nothing with the result.

if (!string.IsNullOrEmpty(this.PostBackUrl))
{
    string postbackReference = this.Page.ClientScript.GetPostBackEventReference
    (new PostBackOptions(this,"",this.PostBackUrl,this.AutoPostBack,false,false,
    false,false,this.ValidationGroup));
    postbackReference = ""; //do nothing with the result.

    startupscript.AppendFormat(",\n            action: '{0}'",
            ResolveClientUrl(this.PostBackUrl));
}

Another thing that can be helpful is to make a JavaScript class a regular Component that is managed automatically by the current instance of ScriptManager and can be found throw the function $find(). And of course, we need to register the class as a Component. To add a regular Component to the page, we need to render something like the code below (the italic text can be changed):

Sys.Application.add_init(function() {
    new function () {
        var component = new MyControls.Web.AjaxUpload($get('addImage'), {
            name: 'AjaxAploadExtender1',
            action: 'CrossPostBack/saveupload.aspx',
            onSubmit: OnSubmitFunction,
            onComplete: OnCompleteFunction,
            autoSubmit: true,
            filter: [{ extensions: ['jpg','bmp','gif','png'],
        description: 'Supported Images Types (*.jpg;*.bmp;*.gif;*.png)' }],
            onUnsupportedExtention: OnUnsupportedFunction,
            validationGroup: ''
        });
        var app = Sys.Application;
        var creatingComponents = app.get_isCreatingComponents();
        component.beginUpdate();
        component.set_id('AjaxAploadExtender1');
        Sys.Application.addComponent(component);
        if (creatingComponents) app._createdComponents
        [app._createdComponents.length] = component;
        component.endUpdate();
        return component;
    };
});

History

  • Wednesday, January 25, 2012
    • First version
  • Thursday, February 9, 2012
    • Enhanced the examples to include exception handling and ResponseType
    • Added the properties SubmissionFrameType and GetSubmissionFrameFunction to control the response direction

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ali Al Omairi(Abu AlHassan)

Software Developer
KASHA
Jordan Jordan

Member

Follow on Twitter Follow on Twitter
-- Life is Good.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionIn Christmas comes, in order to thank everyone, characteristic, novel style, varieties, low price and good quality, and the low sale price. Thank everyone ====( http://www.fullmalls.com )===== ====( http://www.fullmalls.com )===== $33 True Relig Pinmemberdsgfwepogkw2:56 10 Feb '12  
GeneralMy vote of 5 Pinmemberdpsol10:22 3 Feb '12  
QuestionHow about exception handling? Pinmemberdpsol10:08 3 Feb '12  
AnswerRe: How about exception handling? PinmemberAli Al Omairi(Abu AlHassan)14:34 3 Feb '12  
GeneralMy vote of 5 Pinmembershafaqat309_13:36 2 Feb '12  
GeneralRe: My vote of 5 PinmemberAli Al Omairi(Abu AlHassan)14:35 3 Feb '12  
GeneralMy vote of 5 PinmemberRob Craig20129:16 27 Jan '12  
GeneralRe: My vote of 5 PinmemberAli Al Omairi(Abu AlHassan)3:19 28 Jan '12  
GeneralMy vote of 5 PinmemberAbinash Bishoyi4:13 27 Jan '12  
GeneralRe: My vote of 5 PinmemberAli Al Omairi(Abu AlHassan)3:17 28 Jan '12  
Questionwaiting 10 seconds Pinmemberdigimanus2:01 27 Jan '12  
AnswerRe: waiting 10 seconds PinmemberAli Al Omairi(Abu AlHassan)3:15 28 Jan '12  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120222.1 | Last Updated 9 Feb 2012
Article Copyright 2012 by Ali Al Omairi(Abu AlHassan)
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid