HTML5 Drag and Drop file upload plugin for the Grails framework
Pull request Compare This branch is 34 commits behind 4np:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


This plugin allows dragging and dropping files on your browser window to upload them. It works out of the box, and it is also possible to add custom (javascript / ajax) event handlers and add initial files.


Screenshot showing 6 files being uploaded (2 are done and 4 uploads in progress):


Screenshot showing 7 files being uploaded, separated into two pages (maximum of 5 uploads visible):



Reviewed in the Plugin Corner of GroovyMag Volume 6, Issue 8, July 2013:

The Uploadr plugin is a short cut way to implement and use the Drag & Drop features of HTML5. This plugin provide
numerous parameters that can be configured as per need. Also, this plugin supports rating, voting and downloading
of files. It provides event handlers by which you can add your customized code. It also supports i18n messages so,
overall it’s a complete package for uploading files using HTML5 as a platform underneath it.


Build status

build status


  • upload files by
  • * dragging & dropping files onto the uploadr element
  • * button (optional)
  • pagination (customizable number of uploads per 'page')
  • sound effects (disablable)
  • * file upload complete
  • * transfer aborted
  • * file deleted
  • possibility to deny files over a certain file size (optional)
  • rating (5 stars) (optional)
  • voting (like / unlike) (optional)
  • color picker (change background colors) (optional)
  • upload progress
  • * background progress
  • * calculated file upload speed
  • * estimated file upload duration
  • adding default files (e.g. files uploaded in a previous session)
  • customizable css
  • internationalization through i18n
  • custom JavaScript event handlers for:
  • * onStart - start file upload
  • * onProgress - file upload progress
  • * onSuccess - file upload was successful
  • * onFailure - file upload failure
  • * onAbort - user aborted transfer
  • * onView - user clicked 'view'
  • * onDownload - user clicked 'download'
  • * onDelete - user clicked 'delete'
  • * onLike - user clicked 'like'
  • * onUnlike - user clicked 'unlike'
  • * onChangeColor - user picked a color in the color picker
  • built for Grails' resources plugin
  • JavaScript developed as a jQuery plugin


browser supported
Safari supported (as of version ?)
Chrome supported (as of version ?)
Firefox supported (as of version ?)
Internet Explorer supported as of version 10.0.8102.0
Opera not supported

This plugin heavily relies on the HTML5 Drag and Drop and File API's which Microsoft has unfortunately only implemented in Internet Explorer 10.0.8102.0 (part of the Windows 8 developer preview distribution).


Add a compile time dependency to your Grails project's grails-app/conf/Buildconf.groovy:

plugins {
    runtime ":hibernate:$grailsVersion"
    build ":tomcat:$grailsVersion"
    runtime ":jquery:latest.integration"
    runtime ":resources:latest.integration"
    compile ":uploadr:latest.integration"

Grails versions Pre 2.x:

grails install-plugin uploadr


The plugin incorporates a demo tag which demonstrates some examples of how to use the uploadr tag with examples and source code. You can see a live (continuous integration) demo here:


As the uploadr plugin depends on the resources plugin to pull in dependencies, your project should use the resources plugin as well (as will be the standard in Grails 2.x). If you are not familiar with the resources plugin, the demo tag can be used in a view as follows:*

    <r:require modules="uploadr"/>

Adding an uploadr to your view

This tag will initialize the uploadr

<uploadr:add name="myUploadrName" path="/my/upload/path" direction="up" maxVisible="8" unsupported="/my/controller/action" rating="true" voting="true" colorPicker="true" maxSize="204800" />


parameter description example default required
name a unique name for your uploadr myFirstUploadr random uuid yes
path the upload path, this may be a temporary path /tmp none yes
direction manages whether new files will be added on top or on bottom (up/down) up down no
maxVisible determines how many files should be visible and handles pagination 5 0 (=unlimited) no
unsupported shown when unsupported browser is used /my/controller/action default no
class override the default uploadr stylesheet with your own implementation demo uploadr no
noSound disable sound effects noSound="true" false no
rating enable / disable rating rating="true" false no
voting enable / disable voting voting="true" false no
colorPicker enable / disable colorPicker colorPicker="true" false no
viewable enable / disable view button viewable="false" true no
downloadable enable / disable download button downloadable="false" true no
deletable enable / disable delete button deletable="false" true no
maxSize max allowed size in bytes maxSize="204800" 0 (=unlimited) no
allowedExtensions only allow these extensions to be uploaded allowedExtensions="gif,png,jpg,jpeg" "" (= all) no
controller use your own controller to handle file uploads controller="myController" default controller/action no
action use your own action to handle file uploads action="myAction" default controller/action no
plugin plugin that contains your custom controller/action plugin="myPlugin" default plugin no

A screenshot of how the maxSize parameter (maxSize="204800") is handled in the front end:


A screenshot of the default warning when an unsupported browser is used. This can be changed by setting the unsupported parameter to load your own warning or fallback upload support (e.g. unsupported="${createLink(plugin: 'uploadr', controller: 'upload', action: 'warning')}"):


Adding initial files

You could just use the uploadr as an upload facility, but it can also be used to show a list of existing files you already uploaded previously and allow the user to view, download or delete the files. To add initial files you can use the uploadr:file tag as follows:

    <uploadr:file name="${}">
        <uploadr:fileId>myId-${RandomStringUtils.random(32, true, true)}</uploadr:fileId>

If you would like to show the list of files in a certain folder on disk, you could do that by doing something like this (also see the demo tag's second example) although it would be better to pass the file list from the controller to your view:

<% def path = new File("/path/to/folder") %>
<uploadr:add name="mySecondUploadr" path="${path}" direction="up" maxVisible="5" unsupported="${createLink(plugin: 'uploadr', controller: 'upload', action: 'warning')}">
<% path.listFiles().each { file -> %>
    <uploadr:file name="${}">
        <uploadr:fileId>myId-${RandomStringUtils.random(32, true, true)}</uploadr:fileId>
<% } %>

Of course in MVC one should not put logic in a view, and the above examples are merely here to provide some insight. Normally you would pass -for example- a files variable to your view from your controller. So the example would be something like:

<uploadr:add name="mySecondUploadr" path="${path}" direction="up" maxVisible="5" unsupported="${createLink(plugin: 'uploadr', controller: 'upload', action: 'warning')}">
<g:each in="${files}" var="file">
    <uploadr:file name="${}">
        <uploadr:fileId>myId-${RandomStringUtils.random(32, true, true)}</uploadr:fileId>

note though that these examples use RandomStringUtils which you should include:

<%@ page import="org.apache.commons.lang.RandomStringUtils" %>

To override the color of a file you can user the color attribute:


To hide the delete icon / action set the delete attribute to false. Note that if the global deletable parameter is set to false (in the uploadr tag), all delete buttons will be disabled / hidden. This attribute can be used to disable the delete button on a per-file basis:


To set a rating, use the rating attribute:


To add a rating tooltip text, use the ratingText attribute:

        <uploadr:ratingText>This is the tooltip text of the rating for Assignment 2.pdf</uploadr:ratingText>

Screenshot of most of the features enabled and shown (e.g. pagination, color picker, voting, rating, tooltips, buttons):


Event handlers

By default the uploadr is fully functional as is, but it is possible to add your own event handles for certain types of events:

    <!-- upload event handlers //-->
        console.log('start uploading \'' + file.fileName + '\'');
        console.log('\'' + file.fileName + '\' upload progress: ' + percentage + '%');
        return true; // return false to disable default progress handler
        console.log('done uploading \'' + file.fileName + '\', setting some random file id for demonstration purposes');
        console.log('response was:');

        var text = "";
        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        for( var i=0; i < 12; i++ ) text += possible.charAt(Math.floor(Math.random() * possible.length));

        // set a random file id for demonstration purposes
        file.fileId = 'my-random-id::'+text;

        // set file to non-deletable (we do not get a delete icon)
        file.deletable = false;
        console.log('set file.deletable to false so the delete icon will not be shown');

        // override the background to purple (same as initial files)
        $('.progress',domObj).css('background-color', '#f594cc');
        console.log('and overrided the background color to #f594cc');

        // and set the rating tooltip text for the rating
        file.fileRatingText = 'you just uploaded this file and in the onSuccess handler the rating tooltip text is added';

        // callback when done
        console.log('failed uploading \'' + file.fileName + '\'');

    <!-- user triggered event handlers //-->
        console.log('aborted uploading \'' + file.fileName + '\'');
        console.log('you clicked view:');
        console.log('you clicked download:');
        console.log('you clicked delete:');

        // return true / false whether it was successful
        return true;
            console.log('you clicked like:');

            // callback if like action was successfull
            // and pass the new file rating
            callback(file.fileRating + 0.1);
            console.log('you clicked unlike:');

            // callback if unlike action was successfull
            // and pass the new file rating
            callback(file.fileRating - 0.1);
            console.log('you changed the color to:');

            // you can perform an ajax call here
            // to update the color in the back-end
            // for this file

Internationalization / custom texts

The text labels the plugin uses are stored in i18n messages, which can be overwritten / internationalized in your own application:

# labels that appears in the uploadr's percentage text when
# an upload is complete, failed or aborted
uploadr.label.maxsize=too large

# label for the 'select files' button to upload files

# placeholder text
uploadr.placeholder.text=Drag and drop files here to upload...

# badge tooltip
uploadr.badge.tooltip.singular=%d file is still being uploaded...
uploadr.badge.tooltip.plural=%d files are still being uploaded...

# error messages / tooltips
uploadr.error.maxsize=The upload size of %s is larger than allowed maximum of %s

# file control tooltips
uploadr.button.delete=Click to delete this file
uploadr.button.delete.confirm=Are you sure you want to delete this file?
uploadr.button.abort=Click to abort file transfer
uploadr.button.abort.confirm=Are you sure you would like to abort this tranfer? to download this file
uploadr.button.view=Click to view this file to like
uploadr.button.unlike=Click to unlike
uploadr.button.color.picker=Click to change background color
uploadr.button.remove=Click to remove this aborted transfer from your view

In addition to these default labels you can overwrite the placeholder text (the text inside the drop area) and the the select text (the text on the file upload link) on a per-uploadr basis using the following tags:

<uploadr:add ... placeholder="Behold: the drop area!" fileselect="Behold: the fileselect!" ...>

Disabling sound effects

The plugin plays some sound effects whether a file upload was completed, aborted or has failed. To disable these sound effects use the noSound parameter:

<uploadr:add ... noSound="true" ...>

Passing custom variables to the controller

Since version 0.7.3 it is possible to pass variables to the controller. This requires that you implement your own controller to handle the uploaded files, and handle the custom controller variables.

<uploadr:add name="" path=""model="[booleanOne:true, variableTwo: 'foo', variableThree: 'bar', variableFour: 4, myObject: someObject]" />

In the controller you can access the passed model:

        def name        = URLDecoder.decode(request.getHeader('X-Uploadr-Name'), 'UTF-8') as String
        def info        = session.getAttribute('uploadr')
        def myInfo      = (name && info && info.containsKey(name)) ? : [:]
        def model       = (myInfo.containsKey('model')) ? myInfo.model : [:]

Advanced usage: Creating your custom controller to handle file uploads

While the default controller works out of the box, your project's requirements might require a custom plugin. For example, if you would like to deploy your application on cloudfoundry, you need to create your own controller as you do not have file access. In order to accomplish this you can write your own controller, and specify to use it in your uploadr tag:

<uploadr:add ... controller="myController" action="myAction" ...>

To have the file uploads being handled by myAction action of myController.

<uploadr:add ... controller="myController" action="myAction" plugin="myPlugin" ...>

This would make the uploadr use the myAction of myController in myPlugin.

View the default controller's handle action to get an idea of how to implement your own controller to handle file uploads (note that the JavaScript expects JSON).

If you do this, you probably also want to create your own code to view, download and delete uploaded files. You can do this by creating your own event handlers (respectively: onView , onDownload and onDelete ).

For example, if you store your files in a custom controller using MongoDB's GridFS, you could create your own download handler by using the onDownload event handler:

            uploadr.onDownload {
                                // render a template (separation of concerns)
                out << g.render(template:'/js/uploadr/onDownload', model:[])

where the view /js/uploadr/onDownload contains the following code:'<g:createLink plugin="myPlugin" controller="uploadedFile" action="downloadUploadedFile"/>?fileId='+file.fileId);

and the uploadedFile controller's downloadUploadedFile action in myPlugin contains something like the following:

    def downloadUploadedFile = {
        UploadedFile uploadedFile = UploadedFile.get(params.fileId)
        if (!uploadedFile) response.sendError(404, "No uploaded file could be found matching id: ${params.fileId}.")

        GridFSFile gridFSFile = uploadedFile.file
        if (!gridFSFile) response.sendError(404, "No file attached to UploadedFile")

        response.setHeader("Content-disposition", "attachment; filename=\"${uploadedFile.fileName}\"")
        response.setContentLength(uploadedFile.fileSize as int)

        // define input and output streams
        InputStream inStream = null
        OutputStream outStream = null

        // handle file download
        try {
            inStream = gridFSFile.inputStream
            outStream = response.getOutputStream()

            while (true) {
                synchronized (buffer) {
                    int amountRead =
                    if (amountRead == -1) {
                    outStream.write(buffer, 0, amountRead)
        } catch (Exception e) {
            // whoops, looks like something went wrong
            println "download failed! ${e.getMessage()}"
        } finally {
            if (inStream != null) inStream.close()
            if (outStream != null) outStream.close()

        return false

Interacting with an already initialized Uploadr

There are several occasions you might want to be able to interact with an already initialized Uploadr, for example in ajax calls or in your handlers. At the moment only clearing out (and / or erasing all uploaded files) is supported. If you have any feature requests, you can submit them here.

Clearing out an already initialized Uploadr

In several occasions it might be useful to be able to hook into an already initialized Uploadr to perform certain actions (e.g. after an Ajax call). As of version 0.7.6 it is possible to clear out an already initialized uploadr:


By default the call will play a delete sound and execute the onDelete handler (effectively deleting the files on the back-end as well). This default behaviour can be customized by passing in options:

    sound: true,
    erase, false

The following options are available for customization:

option description default
sound play delete sound true
erase erase the file (execute onDelete handler) as well true

Take a look at the documentation above, and the default event handlers in the uploadr initialization JavaScript for more information on how to create your own back-end logic to handle file upload, download, view and delete events.

jQuery plugin

The front-end side (the gui) of the upload plugin is developed as a jQuery plugin (javascript: full, minified, css: full, minified) which means you can also use the front-end in non-Grails projects. You will, however, have to create your own back-end logic (take the handle method in the default controller as an example) to handle the file uploads. The use of the jQuery plugin is currently undocumented, but the initialization JavaScript will probably provide you with all the information you require...


Version 0.8.0 /

  • fixed security flaw #21 in the default file download action where it was possible to use a path traversal attack. Everybody is advised to upgrade to! (Again, thanks to Murf80 for reporting this issue!)

Version 0.7.6 /

  • fixed issues #17 and #18 regarding empty files (thanks to Murf80)
  • added support for clearing the uploadr in ajax calls (#16, thanks for bringing this up Viseth)

Version 0.7.5

Removed obsolete Quartz job (thanks to Luka #13)

Version 0.7.4

Now passing the 'response' variable to the onSuccess handler (by suggestion of domurtag #12). See demo tag's example 4 for an example.

Version 0.7.3

Added the possibility to suply the controller with custom variables:

<uploadr:add name="" path=""model="[booleanOne:true, variableTwo: 'foo', variableThree: 'bar', variableFour: 4, myObject: someObject]" />

As passing variables to the controller is a custom operation, you will need to implement your own controller to handle the uploaded files (thanks to Tom #9 👍).

see demo example 2

Version 0.7.2

Got rid of an error in Grails < 2.2.0 (#8)

Version 0.7.1

As the plugin can be run standalone in demonstration mode (in development and ci), a Quartz jub runs on the background to keep the upload folder clean for demonstration purposes. In previous versions the job would run when Quartz was installed, which is not the appropriate behaviour. It should only run the job when run standalone, in development and ci (see Config.groovy for configuration options). Thanks again Dmitry (#7 :)


I forgot to minify the Javascript in the 0.7.0 release...

Version 0.7.0

Added unicode support (thanks to Dmitry, see #6 )

Version 0.6.1

  • Upgrade to Grails 2.2.0 and changed dependencies to provided / c
  • removed dependency on jquery-ui (resolved #4 - thx fdammassa!)


Bugfixed plugin description

Version 0.6.0

  • Removed obsolete svn keywords which were oddly sometimes causing compilation problems
  • Added support for an allowedExtensions parameter comma separated list (feature request #1). When undefined/empty, all file uploads are allowed.



Note that you should not add spaces to the allowed extensions parameter's value as spaces are evaluated as regular characters. This means that "jpg, gif, png" does not work and will try to validate the . png extentions (bla. png) instead of the desired .png (bla.png).

This new feature has also introduced two new i18n internationalization labels, namely:

uploadr.error.wrongExtension=You tried to upload a file with extension "%s" while only files with extensions "%s" \
  are allowed to be uploaded

example user feedback

Version 0.5.11

Upgraded to Grails 2.0.4 and Grails Central

Version 0.5.10

Minor bugfix with casting string to double in Grails 2.0.1

Version 0.5.9

Dependency map used >= which was wrong, changed it into >

Version 0.5.8

Added grails 2.0 dependency configuration

Version 0.5.7

Added three global parameters (downloadable, deletable, viewable) to define whether the file control buttons are visible (default) or not. Thanks to Michael Aube for the feedback :)

Version 0.5.6

If the uploadr is put in a jquery-ui tab ( there seems to be an issue with hiding elements by duration (e.g. $('myElement').hide(1000);) which is ignored. This resulted in 'done' to remain visible in the percentage div when used in combination in a jquery-ui tab. To resolve this, the div is now explicitly hidden when the transition supposedly finished (e.g. $('myElement').hide(1000,function() { $(this).hide(); }); )

Version 0.5.5

FireFox drag and drop issue was fixed in 0.5.1 (see below), however the same issue apparently still existed in the 'click to upload' button

Version 0.5.4

confirmed the File and Drag & Drop HTML5 API's to be working in Internet Explorer 10 10.0.8102.0 (part of Windows 8). Updated browser check to reflect this support, and improved the default warning message for unsupported browsers.

Version 0.5.3

added support for maxSize argument (in bytes, so 204800 is 200KB). When a user tries to upload a file that is larger than the maximum size, the upload is not performed and a warning is shown. By default there is no limit (maxSize=0). Three i18n texts were added (uploadr.error.maxsize, uploadr.label.maxsize & uploadr.button.remove). Added some tweaks that rating and voting does not show when an upload was not successful, and that a delete button will show to remove the failed / aborted transfer from view. Added maxSize limitation of 200KB to Example 3 in the <uploadr:demo/> tag.

Version 0.5.2

Fixed an issue where some file tags did not always work properly (color, rating, ratingText and deletable)

Version 0.5.1

Implemented support for the changed Firefox 7 File API. While in the previous versions (and in webkit based browsers) the file information was stored in file.fileSize , file.fileName and file.contentType , Firefox 7's File API now uses , file.size and file.type instead. Implemented a fix to support this new behavior.

Build status

build status


Copyright 2011 Jeroen Wesbeek

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.