Wednesday, August 24, 2011

Handling HTML5 "Multiple" File-Inputs With Grails

HTML5 includes a multiple attribute on file inputs; when set, a user can select multiple files to upload for that input with a single Browse... dialog. Grails makes handling uploads from non-multiple file-inputs really easy. For example, here's an action that echoes the content of a file uploaded via an input named myfile:

class MyController { def echoSimple = { def charset = (params.myfile?.contentType =~ /charset=([^;]+)/). collect { it[1].trim() }.join('') ?: 'ISO-8859-1' def content = new String(params.myfile?.bytes ?: ''.bytes, charset) render contentType: params.myfile?.contentType ?: 'text/plain', text: content } }

Handling multiple file-inputs is still pretty easy, but instead of accessing file objects via the controller's params map, you use the multiFileMap property of the controller's request property. File objects will be instances of spring's MultipartFile interface; for multipart form posts, the request property will be an instance of spring's MultipartRequest interface. The multiFileMap property is a map of file-input names to the list of MultipartFiles uploaded by that input. So if you have a form like the following:

<form action="${g.createLink(controller:'my', action:'echoMultiple')}" method="post" enctype="multipart/form-data"> <input type="file" name="myfile" multiple> <button type="submit">Submit</button> </form>

You can echo the content of all the files selected by the user with this action:

class MyController { def echoMultiple = { def content = request.multiFileMap?.myfile?.collect { file -> def charset = (file.contentType =~ /charset=([^;]+)/). collect { it[1].trim() }.join('') ?: 'ISO-8859-1' new String(file.bytes, charset) }?.join('\n') ?: '' render contentType: 'text/plain', text: content } }

(Note that while other browsers have supported the multiple attribute for some time, no version of IE currently supports it — although it should be supported in IE 10.)