Uploading files using Ajax via AlloyUI

You will need the following steps to achieve uploading of files using Ajax via AlloyUI

 

  1. Create a new Liferay Plugin Project using Liferay MVC as the portlet framework.
  2. Create a new Liferay Portlet, named as DocManager for example.
  3. Now open the liferay-portlet.xml file and insert the tag <ajaxable>true</ajaxable> just below the <icon> tag for DocManager portlet.

Now we can focus on our real task.

Creating the UI

<aui:form name="myForm" enctype="multipart/form-data">
    <aui:input type="file" name="myFile" label="My File"></aui:input>
    <aui:button type="submit" name="btnUploadFile" value="Upload File"></aui:button>
</aui:form>

Note the enctype="multipart/form-data". This is needed for a file upload form.

 The AlloyUI script.

The next step will be to write the AlloyUI code to handle the 'Upload File' button and send the file to the server.

You will need to enable the aui-io-request module in order to use io API.

AUI().use(
    'aui-io-request',
    function(A){

});

 

After that you will write the code to handle the button click like below

 

var btnUploadFile = A.one("#<portlet:namespace />btnUploadFile");
        
btnUploadFile.on("click", uploadFile);

Here uploadFile will be the button click handler in our case. The uploadFile is the important function for us. It does the real work. The code for it is as below.

 

function uploadFile(event){
            event.preventDefault();
            var myForm = A.one("#<portlet:namespace/>myForm");
            var ajaxURL = "<portlet:resourceURL id='UPLOAD_FILE'></portlet:resourceURL>";
            
            var configs = {
                method: 'POST',
                form: {
                    id: myForm,
                    upload: true
                },
                sync: true,
                on: {
                    complete: function(){
                        alert("File Upload Complete!");
                        displayFiles();
                    }
                }
            };
            
            A.io.request(ajaxURL, configs);    
        }

Let us review this in detail.

First of all we call preventDefault so that the default functionality of the button is not invoked. For example a submit button might submit the form in the noraml way resulting into a post back.

Next, we take the node reference for the form which is straighforward.

var myForm = A.one("#<portlet:namespace/>myForm");

In the next line we build the url using the portlet:resourceURL tag.

var ajaxURL = "<portlet:resourceURL id='UPLOAD_FILE'></portlet:resourceURL>";
           

This url will be used by our io request that is the ajax call for the file upload. The important thing to note here is the id for this tag. This id is used by our code in the serveResource portlet method to determine the different type of actions to be performed.

In the next line we build the configurations for the io request. The important piece is

form: {
                    id: myForm,
                    upload: true
                },

Here we are assigning the form node reference to the id: property and setting the upload: property to be true. This will add all our form data to the ajax request including the file data as well.

After building the configuration object we call A.io.request(ajaxURL, configs); method.

 

Implementing the backend handler

An io request goes to the serveResource method in our portlet. In there we have the following code

String resourceId = resourceRequest.getResourceID();
        _log.info(resourceId);
        if(resourceId != null && resourceId.equals("UPLOAD_FILE")){
            uploadFile(resourceRequest, resourceResponse);
        }

This will retrieve the resource id and if it is "UPLOAD_FILE" it will call the uploadFile method. The uploadFile method is as below.

 

private void uploadFile(ResourceRequest resourceRequest,
            ResourceResponse resourceResponse) throws IOException {
        UploadRequest uploadRequest = PortalUtil.getUploadPortletRequest(resourceRequest);
        File objFile = uploadRequest.getFile("myFile");
        String objFileName = uploadRequest.getFileName("myFile");
        _log.info("objFile: [" + objFile + "], objFileName: [" + objFileName + "]");        
        String uploadDirectory = "L:/myfiles/";

        File currentFile = new File(uploadDirectory);
        if (currentFile.exists()) {
            _log.info("Going to write the file contents");
            File newFile = new File(uploadDirectory + "/" + objFileName);
            OutputStream os = new FileOutputStream(newFile);
            InputStream is = new FileInputStream(objFile);
            
            byte[] buff = new byte[is.available()];
            is.read(buff);
            os.write(buff);
            is.close();
            os.close();
            
        }
    }   

It retireves the UploadRequest object from the ResouceRequest and then processes the file using it.

The code is straightforward, it checks whether the directory to which we want to upload exists? If it exists, it just copies the files to that directory.

 

The important thing to note is that for file upload A.io.request operation we don't have the success and failure events. So they will not get fired.

In order to confirm our upload of the file one option is to list down the files from the upload directory.

To achieve that you can have the following in the jsp page

function displayFiles(){
           
            var ajaxURL = "<portlet:resourceURL id='FILES_LIST'></portlet:resourceURL>";
            var filesList = A.one("#files-list");
            var configs = {
                
                on: {
                    success: function(){
                        var response = this.get("responseData");
                        filesList.set("innerHTML", response);
                    }
                }
            };
            
            A.io.request(ajaxURL, configs);
        }

Where files-list is a div id added to the page.

On the server side you will write the following code to idenfity the files list request.

if(resourceId != null && resourceId.equals("FILES_LIST")){
            listFiles(resourceRequest, resourceResponse);
        }

listFiles function will build a list of the files and will the send the response.

private void listFiles(ResourceRequest resourceRequest,
            ResourceResponse resourceResponse) throws IOException {
        StringBuilder responseText = new StringBuilder();
        File uploadDirectoryObj = new File(uploadDirectory);
        if(uploadDirectoryObj.exists()){
            File[] files = uploadDirectoryObj.listFiles();
            responseText.append("<ul>");
            for(File file : files){
                responseText.append("<li>").append(file.getName()).append("</li>");
            }
            responseText.append("</ul>");
        }else{
            responseText.append("No File Found");
        }
        
        sendResponse(resourceRequest, resourceResponse, responseText.toString());
    }

 

On the jsp page you can use innerHTML selector to replace the contents of the list-files div.

This concludes our task of uploading a file using ajax call.

I hope this will be helpful for others.

Blogs
AOA,
Sir I have tried this code, it works perfect when used on a plane jsp. But If I try it in an AUI-Modal (a dialog/ popup) it shows an error in javascript console saying "Uncaught TypeError: w.upload is not a function". Need help on this issue,please!
PS: I am trying to create the form at runtime within the dialog using Y.Node.Create() function.