Ashok Goli
Ashok Goli

Reputation: 5183

Liferay.Upload Component Usage for Multi-file Upload

Developing a custom portlet to upload multiple files in Liferay 6.2. Found the Liferay.Upload component while going through Liferay Source Code in the Document Library Portlet at the following location:

https://github.com/liferay/liferay-portal/blob/master/portal-web/docroot/html/portlet/document_library/upload_multiple_file_entries.jsp

We want to reuse this component, but unable to find any documentation regarding the usage.

  1. How do we use the Liferay.Upload component? What do each of the inputs mean & do?
  2. Can we re-use this Liferay.Upload in our custom portlet?
  3. Any concrete documentation on the usage of Liferay.Upload?
  4. Any already implemented portlets available on the web with the Source Code available on the web?

Given below is the extract of the usage of the Liferay Upload component:

<aui:script use="liferay-upload">
    new Liferay.Upload(
        {
            boundingBox: '#<portlet:namespace />fileUpload',
            deleteFile: '<liferay-portlet:actionURL doAsUserId="<%= user.getUserId() %>"><portlet:param name="struts_action" value="/document_library/edit_file_entry" /><portlet:param name="<%= Constants.CMD %>" value="<%= Constants.DELETE_TEMP %>" /><portlet:param name="folderId" value="<%= String.valueOf(folderId) %>" /></liferay-portlet:actionURL>&ticketKey=<%= ticket.getKey() %><liferay-ui:input-permissions-params modelName="<%= DLFileEntryConstants.getClassName() %>" />',
            fileDescription: '<%= StringUtil.merge(PrefsPropsUtil.getStringArray(PropsKeys.DL_FILE_EXTENSIONS, StringPool.COMMA)) %>',
            maxFileSize: '<%= PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) %> B',
            metadataContainer: '#<portlet:namespace />commonFileMetadataContainer',
            metadataExplanationContainer: '#<portlet:namespace />metadataExplanationContainer',
            namespace: '<portlet:namespace />',
            tempFileURL: {
                method: Liferay.Service.bind('/dlapp/get-temp-file-entry-names'),
                params: {
                    groupId: <%= scopeGroupId %>,
                    folderId: <%= folderId %>,
                    tempFolderName: 'com.liferay.portlet.documentlibrary.action.EditFileEntryAction'
                }
            },
            tempRandomSuffix: '<%= EditFileEntryAction.TEMP_RANDOM_SUFFIX %>',
            uploadFile: '<liferay-portlet:actionURL doAsUserId="<%= user.getUserId() %>"><portlet:param name="struts_action" value="/document_library/edit_file_entry" /><portlet:param name="<%= Constants.CMD %>" value="<%= Constants.ADD_TEMP %>" /><portlet:param name="folderId" value="<%= String.valueOf(folderId) %>" /></liferay-portlet:actionURL>&ticketKey=<%= ticket.getKey() %><liferay-ui:input-permissions-params modelName="<%= DLFileEntryConstants.getClassName() %>" />'
        }
    );
</aui:script>

Any pointers are highly appreciated!!

Upvotes: 4

Views: 10986

Answers (4)

evaldeslacasa
evaldeslacasa

Reputation: 603

After trying to customize this component for my needs, I would recommend a different way to implement file uploading in Liferay.

The lack of documentation of the Liferay.Upload Component plus the complexity of the underlaying code of the Liferay File Upload portlet makes it a difficult path to customize the file uploading implemented in Liferay.

My recommendation is the following: create a new custom portlet and use the jQuery library: jQuery File UPload plugin. Visit its website here. It is a well written library with decent documentation, you can adapt it to your needs since it covers a lot of the settings you would expect in a file uploader.

A summary of how the jsp view in your portlet would be:

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui"%>

<portlet:defineObjects />

File Bulk Uploader

<portlet:actionURL var="uploadFileURL" name="uploadFiles"/>

<script>
  $(document).ready(function(){
    $("#multipleupload").uploadFile({
      url:"<%= uploadFileURL.toString() %>",
      multiple:true,
      dragDrop:true,
      sequential:true,
      sequentialCount:1
    });
  });
</script>

<div id="multipleupload">Upload</div>

As you can see, using this library makes a really simple implementation of the view, providing a file uploader. Implement afterwards your portlet class action like this:

public void uploadFiles(ActionRequest request, ActionResponse response) throws PortletException, IOException, SystemException, PortalException{

  UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(request);
  Enumeration<?> paramEnum = uploadRequest.getParameterNames();
  File tempFile;
  Map<String, File> fileMap = new LinkedHashMap<String, File>();

  while (paramEnum.hasMoreElements()){
    String parameter = (String) paramEnum.nextElement();
    if (parameter.startsWith("file")){
      tempFile = uploadRequest.getFile(parameter);

      //******************************************
      //do what you need with the file here
      //******************************************
    }
  }
}

When debugging paramEnum, it gets a "file" parameter each time a dragged and dropped file is processed for upload. This is the sequential mechanism and so, the uploadFiles action is triggered for each file:

paramEnum debugging

Upvotes: 2

Diego87
Diego87

Reputation: 1757

This is my implementation for multiple upload files using liferay style(drag and drop or select file quad). This code will upload the files in a temporary liferay folder (DLFolderConstants.DEFAULT_PARENT_FOLDER_ID), then inside the method METHODJAVANAMEManageForm in ClientPortlet.java you can get them and using as you want. This is the same behaviour of liferay multiple upload files.

view.jsp

<portlet:actionURL windowState="maximized" var="NAMEUrl" name="METHODJAVANAMEManageForm">
</portlet:actionURL>

<form action="<c:out value="${NAMEUrl}"/>" method="POST" enctype="multipart/form-data">
    <liferay-util:include page="/jsp/multipleAttach.jsp" servletContext="<%=this.getServletContext()%>"/>
    <!--other <input <div <span.....-->
</form>

multipleAttach.jsp:

<%@page import="com.liferay.portal.kernel.util.ParamUtil"%>
<%@page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>
<%@page import="com.liferay.portal.kernel.util.StringPool"%>
<%@page import="com.liferay.portal.kernel.util.PropsKeys"%>
<%@page import="com.liferay.portal.kernel.util.PrefsPropsUtil"%>
<%@page import="com.liferay.portal.kernel.util.StringUtil"%>
<%@page import="com.liferay.portal.service.TicketLocalServiceUtil"%>
<%@ page import="com.liferay.portlet.documentlibrary.model.DLFileEntryConstants" %>
<%@ page import="com.liferay.portal.kernel.util.Constants" %>
<%@page import="com.liferay.portal.service.ServiceContext"%>
<%@page import="com.liferay.portal.model.TicketConstants"%>
<%@page import="com.liferay.portal.model.User"%>
<%@page import="com.liferay.portal.model.Ticket"%>
<%@page import="com.liferay.portal.kernel.repository.model.Folder"%>
<%@page import="com.sun.mail.imap.DefaultFolder"%>
<%@page import="com.liferay.util.portlet.PortletProps"%>
<%@page import="com.liferay.portlet.documentlibrary.model.DLFolderConstants"%>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui"%>
<%@ taglib uri="http://liferay.com/tld/util" prefix="liferay-util"%>
<%@ taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet"%>
<%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme"%>
<%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui"%>
<portlet:defineObjects />
<liferay-theme:defineObjects/>

 <%
Ticket ticket = TicketLocalServiceUtil.addTicket(user.getCompanyId(), User.class.getName(), user.getUserId(), TicketConstants.TYPE_IMPERSONATE, null, null, new ServiceContext());
%>

<!--REEDME instead of<form> of view.jsp you can also use <aui:form action="<c:out value="${NAMEUrl}"/>" cssClass="lfr-export-dialog" method="post" name="fm1">-->

<div class="lfr-dynamic-uploader">
    <div class="lfr-upload-container" id="<portlet:namespace />fileUpload">
    &nbsp;
    </div>
</div>
<div id="<portlet:namespace/>fallback"></div>

<aui:script use="liferay-upload,aui-base">
    var liferayUpload = new Liferay.Upload(
        {
            boundingBox: '#<portlet:namespace />fileUpload',
            deleteFile: '<liferay-portlet:actionURL name="METHODJAVANAMEFordeleteFile" doAsUserId="<%= user.getUserId() %>"><portlet:param name="struts_action" value="/document_library/edit_file_entry" /><portlet:param name="<%= Constants.CMD %>" value="<%= Constants.DELETE_TEMP %>" /><portlet:param name="folderId" value="<%= String.valueOf(DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) %>" /></liferay-portlet:actionURL>&ticketKey=<%= ticket.getKey() %><liferay-ui:input-permissions-params modelName="<%= DLFileEntryConstants.getClassName() %>" />',
            fileDescription: '<%= StringUtil.merge(PrefsPropsUtil.getStringArray(PropsKeys.DL_FILE_EXTENSIONS, StringPool.COMMA)) %>',
            maxFileSize: '<%= PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) %>',
            metadataContainer: '#<portlet:namespace />commonFileMetadataContainer',
            metadataExplanationContainer: '#<portlet:namespace />metadataExplanationContainer',
            namespace: '<portlet:namespace />',
            tempFileURL: {
                method: Liferay.Service.bind('/dlapp/get-temp-file-entry-names'),
                params: {
                    groupId: <%= scopeGroupId %>,
                    folderId: <%= DLFolderConstants.DEFAULT_PARENT_FOLDER_ID %>,
                    tempFolderName: 'com.example.portlet.ClientPortlet' <!-- this is equals of _TEMP_FOLDER_NAME_ATTACHMENT of ClientPortlet.java -->
                }
            },
            uploadFile: '<liferay-portlet:actionURL name="METHODJAVANAMEForuploadFile" doAsUserId="<%= user.getUserId() %>"><portlet:param name="struts_action" value="/document_library/edit_file_entry" /><portlet:param name="<%= Constants.CMD %>" value="<%= Constants.ADD_TEMP %>" /><portlet:param name="folderId" value="<%= String.valueOf(DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) %>" /></liferay-portlet:actionURL>&ticketKey=<%= ticket.getKey() %><liferay-ui:input-permissions-params modelName="<%= DLFileEntryConstants.getClassName() %>" />'
        }
    );
</aui:script>

ClientPortlet.java

private static final String _TEMP_FOLDER_NAME_ATTACHMENT = ClientPortlet.class.getName();

public void METHODJAVANAMEManageForm(ActionRequest actionRequest,ActionResponse actionResponse) throws IOException, PortletException 
{
    List<FileEntry> tempFileEntrys = new ArrayList<FileEntry>();
    try
    {
        ThemeDisplay themeDisplay  =(ThemeDisplay)actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
        UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(actionRequest);
        //get selected checkbox of file to upload
        String [] selectedFile = uploadRequest.getParameterValues("selectUploadedFileCheckbox");
        if(selectedFile != null)
        {
            for(int i = 0; i < selectedFile.length; ++i)
            {
                FileEntry tmpfile = TempFileUtil.getTempFile(themeDisplay.getScopeGroupId(), themeDisplay.getUserId(), selectedFile[i], _TEMP_FOLDER_NAME_ATTACHMENT);
                if(tmpfile!=null && tmpfile.getTitle()!=null && (!tmpfile.getTitle().equals("")))
                {
                    tempFileEntrys.add(tmpfile);
                    System.out.println("file: " + tmpfile.getTitle());
                }
            }
        }

        /////upload tempFileEntrys where you needed
    }
    catch (Exception e) 
    {
        e.printStackTrace();
    }
    finally
    {
        //delete all tmp files uploaded in liferay tmp folder
        for (FileEntry tmp : tempFileEntrys) 
        {
            try 
            {
                TempFileUtil.deleteTempFile(tmp.getFileEntryId());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    actionResponse.setRenderParameter("mvcPath","/jsp/viewList.jsp");
}

public void METHODJAVANAMEForuploadFile(ActionRequest actionRequest,ActionResponse actionResponse) throws Exception 
{
    //upload file in liferay tmp folder
    UploadPortletRequest uploadPortletRequest = PortalUtil.getUploadPortletRequest(actionRequest);
    ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
    long folderId = ParamUtil.getLong(uploadPortletRequest, "folderId");
    String sourceFileName = uploadPortletRequest.getFileName("file");
    InputStream inputStream = null;
    try 
    {
        inputStream = uploadPortletRequest.getFileAsStream("file");
        String contentType = uploadPortletRequest.getContentType("file");
        DLAppServiceUtil.addTempFileEntry(themeDisplay.getScopeGroupId(), folderId, sourceFileName,_TEMP_FOLDER_NAME_ATTACHMENT, inputStream, contentType);
        JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
        jsonObject.put("name", sourceFileName);
        jsonObject.put("title", sourceFileName);
        writeJSON(actionRequest, actionResponse, jsonObject);
    }
    catch (Exception e)
    {
        UploadException uploadException =(UploadException)actionRequest.getAttribute(WebKeys.UPLOAD_EXCEPTION);
        if ((uploadException != null) &&uploadException.isExceededSizeLimit()) 
        {
            throw new FileSizeException(uploadException.getCause());
        }
        else 
        {
            throw e;
        }
    }
    finally 
    {
        StreamUtil.cleanUp(inputStream);
    }
}       

public void METHODJAVANAMEFordeleteFile(ActionRequest actionRequest,ActionResponse actionResponse) throws IOException, PortletException 
{
    //delete file from liferay tmp folder, before uploaded
    ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
    long folderId = ParamUtil.getLong(actionRequest, "folderId");
    String fileName = ParamUtil.getString(actionRequest, "fileName");
    JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
    try 
    {
        DLAppServiceUtil.deleteTempFileEntry(themeDisplay.getScopeGroupId(), folderId, fileName,_TEMP_FOLDER_NAME_ATTACHMENT);
        jsonObject.put("deleted", Boolean.TRUE);
    }
    catch (Exception e) 
    {
        String errorMessage = themeDisplay.translate("an-unexpected-error-occurred-while-deleting-the-file");
        jsonObject.put("deleted", Boolean.FALSE);
        jsonObject.put("errorMessage", errorMessage);
    }
    writeJSON(actionRequest, actionResponse, jsonObject);
}

Click for Result view

Upvotes: 0

Ashok Goli
Ashok Goli

Reputation: 5183

I gave it a shot based on Pawel's links and succeeded in a sort-of POC.

view.jsp

<%@page import="com.liferay.portal.kernel.util.ParamUtil"%>
<%@page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>
<%@page import="com.liferay.portal.kernel.util.StringPool"%>
<%@page import="com.liferay.portal.kernel.util.PropsKeys"%>
<%@page import="com.liferay.portal.kernel.util.PrefsPropsUtil"%>
<%@page import="com.liferay.portal.kernel.util.StringUtil"%>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui"%>

<portlet:defineObjects />

This is the <b>Liferay File Upload</b> portlet in View mode.

<portlet:actionURL name="uploadFile" var="uploadFileURL" >
    <portlet:param name="jspPage" value="/html/singefileuploadaction/view.jsp" />
</portlet:actionURL>

<portlet:resourceURL var="importPortletURL" id="uploadSubmit">
    <portlet:param name="jspPage" value="/html/singefileuploadaction/view.jsp" />
</portlet:resourceURL>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>


<aui:form action="<%= importPortletURL %>" cssClass="lfr-export-dialog" method="post" name="fm1">

    <div class="lfr-dynamic-uploader">
        <div class="lfr-upload-container" id="<portlet:namespace />fileUpload">
            &nbsp;
        </div>
    </div>
    <div id="<portlet:namespace/>fallback"></div>

        <aui:button-row>
            <aui:button cssClass='hide' name="continueButton" type="submit" value="Continue" />
        </aui:button-row>

    <aui:script use="liferay-upload,aui-base">
        var liferayUpload = new Liferay.Upload({
            allowedFileTypes: '<%= StringUtil.merge(PrefsPropsUtil.getStringArray(PropsKeys.DL_FILE_EXTENSIONS, StringPool.COMMA)) %>',
            container: '#<portlet:namespace />fileUpload',
            maxFileSize: <%=Long.parseLong(PrefsPropsUtil.getString(PropsKeys.DL_FILE_MAX_SIZE)) %> / 1024,
            namespace:'<portlet:namespace />',
            uploadFile: '<%=uploadFileURL.toString()%>',        
            tempFileRemoved: function(){console.log('Temp File Removed');},
            'strings.dropFilesText': 'Drop Files Here to Upload.',
            'strings.dropFileText': 'Drop File Here to Upload.',
            'strings.selectFileText': 'Select File to Upload.',
            'strings.selectFilesText': 'Select Files to Upload.',
            'strings.fileCannotBeSavedText': 'File cannot be saved.',
            'strings.pendingFileText': 'This file was previously uploaded but not actually saved',
            'strings.uploadsCompleteText': 'Upload is complete. Please save.',
            multipleFiles: false

        });

        <!-- ASHOK: !IMPORTANT-DO NOT REMOVE-This code is to re-position the Upload Component HTML code which is placed on top of the page by default: Might be a BUG?? -->
        $( document ).ready(function() {
            $('.component.liferayupload').appendTo("#<portlet:namespace />fileUpload");
        });

        var continueButton = A.one('#<portlet:namespace />continueButton');

        function toggleContinueButton() {
            var uploadedFiles = liferayUpload._fileListContent.all('.upload-file.upload-complete');
            if (uploadedFiles.size() == 1) {
                console.log('One file Upload');
                console.log(uploadedFiles);
                continueButton.show();
            }
            else {
                console.log(uploadedFiles);
                continueButton.hide();
            }
        }


        <!-- Ashok: Upload Component Events BEGIN-->
        <!-- Ashok: Fired when File Upload STARTS-->
        liferayUpload._uploader.on(
                'fileuploadstart',
                function(event) {
                    console.log('File Upload Start');               
                }
            );  
        <!-- Ashok: Fired when File Upload is COMPLETE-->
        Liferay.on(
                'uploadcomplete',
                function(event) {
                    console.log('File Upload Complete');
                }
            );
        <!-- Ashok: Fired when All Uploads are COMPLETE-->
        liferayUpload._uploader.on(
                'alluploadscomplete',
                function(event) {
                    console.log('All Uploads Complete');
                    toggleContinueButton();
                }
            );      
        <!-- Ashok: Fired when Temp file is REMOVED-->
        Liferay.on(
                'tempFileRemoved',
                function(event) {
                    console.log('Temp File Removed');
                    toggleContinueButton();
                }
            );      
        <!-- Ashok: Upload Component Events END-->


        $('#<portlet:namespace />continueButton').on(
            'click',
            function(event) {
                event.preventDefault();

                $('#<portlet:namespace />fm1').ajaxSubmit(
                    {
                        success: function(responseData) {
                            <%-- $('#<portlet:namespace />exportImportOptions').html(responseData); --%>
                        }
                    }
                );
            }
        );
    </aui:script>


</aui:form>

SingeFileUploadAction.java

package com.ashok.liferay.upload;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;

import com.liferay.portal.kernel.upload.UploadPortletRequest;
import com.liferay.portal.util.PortalUtil;
import com.liferay.util.bridges.mvc.MVCPortlet;

/**
 * Portlet implementation class SingeFileUploadAction
 */
public class SingeFileUploadAction extends MVCPortlet {
  public void uploadFile(ActionRequest actionRequest,
          ActionResponse actionResponse) throws IOException, PortletException {
      System.out.println("In SingeFileUploadAction");
      UploadPortletRequest uploadRequest=PortalUtil.getUploadPortletRequest(actionRequest);
      File file =uploadRequest.getFile("file");
      String fileName = uploadRequest.getFileName("file");
      System.out.println("FileName:"+fileName);
      //Mike Test
      Map<String, String[]> reqMap = actionRequest.getParameterMap();
      System.out.println("Printing all actionRequest Params");
      for (Map.Entry<String, String[]> entry : reqMap.entrySet())
      {
          System.out.println(entry.getKey() + "/" + Arrays.toString(entry.getValue()));
      }

      System.out.println("----------\nPrinting all uploadRequest Params");
      Map<String, String[]> upReqMap =uploadRequest.getParameterMap();
      for (Map.Entry<String, String[]> entry : upReqMap.entrySet())
      {
          System.out.println(entry.getKey() + "/" + Arrays.toString(entry.getValue()));
      }

      System.out.println(file.getName());

  }
  public void uploadSubmit(ResourceRequest resourceRequest,
      ResourceResponse resourceResponse)  {
    System.out.println("In uploadSubmit");
    UploadPortletRequest uploadRequest=PortalUtil.getUploadPortletRequest(resourceRequest);
//    File file =uploadRequest.getFile("file");
//    System.out.println(file.getName());

  }
}

Liferay File Upload

Upvotes: 4

Pawel Kruszewski
Pawel Kruszewski

Reputation: 569

It looks like this component is reused all over in Liferay. I think you could reuse it as well. I've found following documentation of initial parameters. I hope it'll help you progress. Good luck!

Upvotes: 1

Related Questions