uploading multiple files with same name
A good binding feature in grails is that when you have multiple input fields with same name, they are available as a list in params
. But this does not hold with html file input fileds. If you have multiple file input fields with same name, params.fieldName
will not return a list but the first input field with name fieldName
. This is due to using fileMap
attribute of request
object. Here is the relevant part of GrailsParameterMap
class(I suspected this class because of params.getClass()
returned GrailsParameterMap
) :
[groovy]
GrailsParameterMap(HttpServletRequest request) {
//………………………..
if (request instanceof MultipartHttpServletRequest) {
def fileMap = request.fileMap
for (fileName in fileMap.keySet()) {
requestMap.put(fileName, request.getFile(fileName))
}
}
//………………………..
}
[/groovy]
Due to above implementation, If you have three file input fields with name familyPics
, only the first one will be available via params.familyPics
of type CommonsMultipartFile
.
In one of the mail thread(links at the end of the blog), I got to know that grails uses Spring 3.0 starting from grails version 1.2 which supports multiFileMap
attribute in request
object. Using this attribute you can access multiple files from inputs with the same name (or that uses the HTML5 multiple attribute). So if the implementation is changed to something like :
[groovy]
GrailsParameterMap(HttpServletRequest request) {
//………………………..
if (request instanceof MultipartHttpServletRequest) {
def multipleFileMap = request.multiFileMap
/* instead of using fileMap. However this map value is always a list even if there is only one file input for a name */
multipleFileMap.each {fieldName, files ->
if (files.size() == 1) {
requestMap.put(fieldName, files.first())
/* Make this available as a single element instead of list, if the user wants he can use params.list(‘fieldName’) for list version */
} else {
requestMap.put(fieldName, files)
}
}
}
//………………………..
}
[/groovy]
Then if there are three file input fields with name familyPics
, then params.familyPics
will return a list of CommonsMultipartFile
objects. If there is only one file input field with name profilePic
then params.profilePic
will return an object of type CommonsMultipartFile
and params.list('profilePic')
will return a list of CommonsMultipartFile
objects.
This feature can be added using Groovy/Grails MetaProgramming(Until grails is updated for this feature). However I went with Grails Filter solution. Create/update a filter with this entry :
[groovy]
multipartFileSupport(controller: ‘*’, action: ‘*’) {
before = {
if (request instanceof MultipartHttpServletRequest) {
def multipleFileMap = request.multiFileMap
multipleFileMap.each {fieldName, files ->
if (files.size() == 1) {
params.put(fieldName, files.first())
} else {
params.put(fieldName, files)
}
}
}
}
}
[/groovy]
Now inside any controller/action you can access statements like(even grails auto binding will work fine) :
[groovy]
/* Suppose there is a file input field with name profilePic */
CommonsMultipartFile profilePic=params.profilePic
List<CommonsMultipartFile> profilePicAsList=params.list(‘profilePic’)
/* Suppose there are three file input fields with same name familyPics */
List<CommonsMultipartFile> familyPics=params.familyPics
[/groovy]
Hope this feature will be available soon in grails without having to write filters or doing meta programming.
Helpful links :
http://stackoverflow.com/questions/3710232/how-to-iterate-over-uploaded-files-in-grails
~~~~~~Cheers~~~~~~~
Bhagwat Kumar
bhagwat(at)intellligrape(dot)com