Nov 10, 2021 2:58:45 PM Thomas Dumont avatar

Integration of Asynchronous Upload into a plugin

The Asynchronous Upload plugin is a plugin to simplify and centralize the asynchronous upload framework used in a Lutèce application. It currently uses the JQuery File Upload JavaScript framework to handle asynchronous file uploads.

Description of operation

The Asynchronous Upload plugin provides JavaScript files and Freemarker macros to create "file" type inputs for asynchronous uploads. The HTML code generated by these macros uses JavaScript functions to interact in AJAX with the server to perform uploads.

The requests made in AJAX are processed by a server-side upload manager (called in the following handler). This handler has two responsibilities :

  • Manage the dialogue between the client JavaScript and the server.
  • Provide uploaded files to the service that requires them, and allow them to add or remove them.

The dialogue with the client is fully implemented by the plugin. Howecer the management of the uploaded files (recording, deletion, ...) is under the responsibility of the application using this plugin. It is not implemented by default.

The standard operation of a form with asynchronous upload file fields is as follows :

  • Display the form to the user.
  • Input of the form by the user, and possible upload of files asynchronously.
  • Submit the form by the user.
  • Receipt of form data submitted by the server.
  • Retrieve the list of uploaded files associated with each field of the form submitted by the service processing the request via the handler.
  • Business processing of data (validation, backup, etc ...).

Using the plugin

In order to use the Asynchronous Upload plugin, you have to perform the following operations :

  • Include the Asynchronous Upload plugin and activate it.
  • Implement a server-side upload manager.
  • Include in the HTML page a javascript file provided by JSP.
  • Add file-type inputs to the form(s) allowing for asynchronous uploads using Freemarker macros.

Inclusion of the plugin

The plugin can be declared as a dependency of another Lutèce plugin in its pom.xml thanks to the following code:

<dependency>
    <groupId>fr.paris.lutece.plugins</groupId>
    <artifactId>plugin-asynchronousupload</artifactId>
    <version>[1.0.1-SNAPSHOT,)</version>
    <type>lutece-plugin</type>
</dependency>

Implementing the upload manager

The upload manager must implement the interface fr.paris.lutece.plugins.asynchronousupload.service.IAsyncUploadHandler. It is strongly recommended to use the abstract class fr.paris.lutece.plugins.asynchronousupload.service.AbstractAsynchronousUploadHandler. In the following we will assume that we are expanding this class.

A handler must therefore at least implement the methods below :

public class UploadHandlerDemo extends AbstractAsynchronousUploadHandler
{
    private static final String HANDLER_NAME = "myHandler";

    / **
     * Check if a list of uploaded files for a given field are valid
     * @param request The request made
     * @param strFieldName The name of the field used to upload a file.
     * @param listFileItemsToUpload List of uploaded files to check.
     * @param locale The locale to use to display eventual error messages
     * @return The error message, or null if no error has been detected and the files are valid.
     * /
    @Override
    public String canUploadFiles (HttpServletRequest request, String strFieldName,
            List <FileItem> listFileItemsToUpload, Local Local)
    {
        // TODO Auto-generated method stub
        return null;
    }

    / **
     * Allows you to declare a file as uploaded. The implementation of this method is now responsible for managing the file.
     * @param fileItem The uploaded file
     * @param strFieldName The name of the field to which the file is associated
     * @param request The request
     * /
    @Override
    public void addFileItemToUploadedFilesList (FileItem fileItem, String strFieldName, HttpServletRequest request)
    {
        // TODO Auto-generated method stub
    }

    / **
     * Allows you to retrieve the list of uploaded files for a given field. The list must be ordered chronologically by date of upload.
     * For each file an index will be associated corresponding to the index of the file in the list (the oldest file will have index 0).
     * Two successive calls of this method must therefore return a list ordered in the same way.
     * @param strFieldName The name of the field from which we want to recover the files
     * @param session the session of the user using the file. Use only if the files are saved in session.
     * @return The list of uploaded files for the given field
     * /
    @Override
    public List <FileItem> getListUploadedFiles (StrFieldName String, HttpSession Session)
    {
        // TODO Auto-generated method stub
        return null;
    }

    / **
     * Allows you to delete a previously uploaded file
     * @param strFieldName The name of the field
     * @param session the session of the user using the file. Use only if the files are saved in session.
     * @param nIndex The index of the file in the list of uploaded files.
     * /
    @Override
    public void removeFileItem (strFieldName String, session httpSession, int nIndex)
    {
        // TODO Auto-generated method stub
    }

    / **
     * Allows you to define the name of the handler. This name must be unique, and contain only numeric characters (no points, comma, ...).
     * It is recommended to prefix the name of the plugin, then to suffix a functional name.
     * Be careful, the name of the handler is different from the name of the associated Spring bean!
     * /
    @Override
    public String getHandlerName ()
    {
        return HANDLER_NAME;
    }
}

The handler must then be declared as a Spring bean in a context file.

Exemple You can find an example of upload manager implementation in the genericattributes plugin here .

Inclusion of the JavaScript file

Once the handler is created, the JavaScript file provided by the JSP jsp/site/plugins/asynchronousupload/GetMainUploadJs.jsp must be included in the HTML page containing the form. This JSP uses the following parameters :

  • handler : Name of the handler (as defined in the service). This parameter is required.
  • maxFileSize : The maximum size (in bytes) of each uploaded file. This parameter is optional, the default is 2097152.

Inclusion example:

<script type="text/javascript" src="jsp/site/plugins/asynchronousupload/GetMainUploadJs.jsp?handler=myAsynchronousUploadHandler" ></script>

Info this file must be included for each handler used on the page.

Freemarker macros

All that remains is to create the input that will enable asynchronous uploads.

These inputs can be created thanks to the macros contained in the template skin / plugins / asynchronousupload / upload_commons.html. Macros in this file are accessible by importing them into your templates. To do this, add the following line, for example in the header of your templates:

<#include "/skin/plugins/asynchronousupload/upload_commons.html" />

Once this file is imported, two macros can be used:

/ **
 * Macro to include the JavaScript and CSS files necessary for the operation of asynchronous upload inputs.
 * This macro must be used if the inclusion service of the Lutèce portal does not work (example: in the Back Office, in specific JSPs, ...)
 * /
<#macro addRequiredJsFiles>

/ **
 * Macro to add an input of file type initialized for asynchronous upload.
 * This macro will also create a block to display the list of upload files. This list will be updated during uploads.
 * @param fieldName Name of the input to generate.
 * @param handler Instance of the associated upload manager service.
 * @param listUploadedFiles List of files that have already been uploaded. Each object in this list must have a 'title' or 'name' attribute.
 * @param inputCssClass CSS class to add to the generated input. The default value is an empty string.
 * @param multiple True to allow simultaneous selection of multiple files, false to prohibit it. Requires an HTML5 compatible browser to work.
 * /
<#macro addFileInputAndfilesBox fieldName handler listUploadedFiles inputCssClass = '' multiple = false>

The macro <#macro addFileInputAndfilesBox fieldName handler listUploadedFiles inputCssClass = '' multiple = false> uses the macros below :

/ **
 * Macro allowing to add an input of file type initialized for the asynchronous upload.
 * @param fieldName Name of the input to generate.
 * @param handler Instance of the associated upload manager service.
 * @param inputCssClass CSS class to add to the generated input. The default value is an empty string.
 * @param multiple True to allow simultaneous selection of multiple files, false to prohibit it. Requires an HTML5 compatible browser to work.
 * /
<#macro addFileInput fieldName handler multiple inputCssClass = false>

/ **
 * Macro creating a block to display the list of upload files. This list will be updated during uploads.
 * @param fieldName Name of the input to generate.
 * @param handler Instance of the associated upload manager service.
 * @param listUploadedFiles List of files that have already been uploaded. Each object in this list must have a 'title' or 'name' attribute.
 * /
<#macro addUploadedFilesBox fieldName handler listUploadedFiles>

You can use the addFileInputAndfilesBox macro to create as many inputs as needed.

The generated HTML code will therefore have an input allowing the asynchronous upload and using the handler service specified in parameter.

Reloading previously uploaded files

If files that have already been uploaded must be added to the list of files uploaded by a user (example: reloading a form, ...), they must be declared to the upload manager. To do this, simply call the IAsynchUploadHandler.addFileItemToUploadedFilesList( FileItem, String, HttpServletRequest ) method of the associated upload manager.

Each of these files will then be considered as having been uploaded by the user. The files will be included in the list of uploaded files, and the user will have the option to delete them as any other file.

Synchronous uploads

Once the asynchronous upload has been set up, all that remains is to set up the possibility for users who do not have JavaScript to use the synchronous upload. This can be set up by server-specific processing.

Adding file

The upload manager's IAsynchUploadHandler.hasAddFileFlag( HttpServletRequest, String ) method is used to check whether a flag indicating that a file has been uploaded for a specific input is present in the request. If this is the case, the IAsynchUploadHandler.addFilesUploadedSynchronously( HttpServletRequest, String ) method is used to add the corresponding files to the upload manager. These will then be treated like any other uploaded file.

For the synchronous upload to work properly, you must do the following :

  • Show the form with the list of already uploaded files initialized
  • When the form is submitted, check if the upload flag is present for each input file.
  • For each flag found, add the files associated with the upload manager.
  • If at least 1 flag is present, display the form again with the list of updated files. If no flag is present, treat the request as a form validation.

Deleting files

Synchronous file deletion works similarly to the upload one : the IAsynchUploadHandler.hasRemoveFlag( HttpServletRequest, String ) method tests the presence of a file deletion flag for a given input, and the IAsynchUploadHandler.doRemoveFile( HttpServletRequest , String ) method allows deletion of specified files.