Important : This question is completely useless for any Spring version higher than 3.0.4 as the issue discussed in this thread had been fixed in that version a long ago and is no longer reproducible in subsequent versions of Spring.
I'm using Spring version 3.0.2. I need to upload multiple files using the multiple="multiple"
attribute of a file browser such as,
<input type="file" id="myFile" name="myFile" multiple="multiple"/>
(and not using multiple file browsers something like the one stated by this answer, it indeed works I tried).
Although no versions of Internet Explorer supports this approach unless an appropriate jQuery plugin/widget is used, I don't care about it right now (since most other browsers support this).
This works fine with commons fileupload but in addition to using RequestMethod.POST
and RequestMethod.GET
methods, I also want to use other request methods supported and suggested by Spring like RequestMethod.PUT
and RequestMethod.DELETE
in their own appropriate places. For this to be so, I have configured Spring with HiddenHttpMethodFilter
which goes fine as this question indicates.
but it can upload only one file at a time even though multiple files in the file browser are chosen. In the Spring controller class, a method is mapped as follows.
@RequestMapping(method={RequestMethod.POST}, value={"admin_side/Temp"})
public String onSubmit(@RequestParam("myFile") List<MultipartFile> files, @ModelAttribute("tempBean") TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) throws IOException, FileUploadException {
for (MultipartFile file : files) {
System.out.println(file.getOriginalFilename());
}
}
Even with the request parameter @RequestParam("myFile") List<MultipartFile> files
which is a List
of type MultipartFile
(it can always have only one file at a time).
I could find a strategy which is likely to work with multiple files on this blog. I have gone through it carefully.
The solution below the section SOLUTION 2 – USE THE RAW REQUEST says,
If however the client insists on using the same form input name such as ‘files[]‘ or ‘files’ and then populating that name with multiple files then a small hack is necessary as follows. As noted above Spring 2.5 throws an exception if it detects the same form input name of type file more than once. CommonsFileUploadSupport – the class which throws that exception is not final and the method which throws that exception is protected so using the wonders of inheritance and subclassing one can simply fix/modify the logic a little bit as follows. The change I’ve made is literally one word representing one method invocation which enables us to have multiple files incoming under the same form input name.
It attempts to override the method
protected MultipartParsingResult parseFileItems(List fileItems, String encoding){}
of the abstract class CommonsFileUploadSupport
by extending the class CommonsMultipartResolver
such as,
package multipartResolver;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.commons.fileupload.FileItem;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
public final class MultiCommonsMultipartResolver extends CommonsMultipartResolver {
public MultiCommonsMultipartResolver() {}
public MultiCommonsMultipartResolver(ServletContext servletContext) {
super(servletContext);
}
@Override
@SuppressWarnings("unchecked")
protected MultipartParsingResult parseFileItems(List fileItems, String encoding) {
Map<String, MultipartFile> multipartFiles = new HashMap<String, MultipartFile>();
Map multipartParameters = new HashMap();
// Extract multipart files and multipart parameters.
for (Iterator it = fileItems.iterator(); it.hasNext();) {
FileItem fileItem = (FileItem) it.next();
if (fileItem.isFormField()) {
String value = null;
if (encoding != null) {
try {
value = fileItem.getString(encoding);
} catch (UnsupportedEncodingException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not decode multipart item '" + fileItem.getFieldName()
+ "' with encoding '" + encoding + "': using platform default");
}
value = fileItem.getString();
}
} else {
value = fileItem.getString();
}
String[] curParam = (String[]) multipartParameters.get(fileItem.getFieldName());
if (curParam == null) {
// simple form field
multipartParameters.put(fileItem.getFieldName(), new String[]{value});
} else {
// array of simple form fields
String[] newParam = StringUtils.addStringToArray(curParam, value);
multipartParameters.put(fileItem.getFieldName(), newParam);
}
} else {
// multipart file field
CommonsMultipartFile file = new CommonsMultipartFile(fileItem);
if (multipartFiles.put(fileItem.getName(), file) != null) {
throw new MultipartException("Multiple files for field name [" + file.getName()
+ "] found - not supported by MultipartResolver");
}
if (logger.isDebugEnabled()) {
logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize()
+ " bytes with original filename [" + file.getOriginalFilename() + "], stored "
+ file.getStorageDescription());
}
}
}
return new MultipartParsingResult(multipartFiles, multipartParameters);
}
}
What happens is that the last line in the method parseFileItems()
(the return statement) i.e.
return new MultipartParsingResult(multipartFiles, multipartParameters);
causes a compile-time error because the first parameter multipartFiles
is a type of Map
implemented by HashMap
but in reality, it requires a parameter of type MultiValueMap<String, MultipartFile>
It is a constructor of a static class inside the abstract class CommonsFileUploadSupport
,
public abstract class CommonsFileUploadSupport {
protected static class MultipartParsingResult {
public MultipartParsingResult(MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams) {}
}
}
The reason might be - this solution is about the Spring version 2.5 and I'm using the Spring version 3.0.2 which might be inappropriate for this version.
I however tried to replace the Map
with MultiValueMap
in various ways such as the one shown in the following segment of code,
MultiValueMap<String, MultipartFile>mul=new LinkedMultiValueMap<String, MultipartFile>();
for(Entry<String, MultipartFile>entry:multipartFiles.entrySet()) {
mul.add(entry.getKey(), entry.getValue());
}
return new MultipartParsingResult(mul, multipartParameters);
but no success. I'm not sure how to replace Map
with MultiValueMap
and even doing so could work either. After doing this, the browser shows the Http response,
HTTP Status 400 -
type Status report
message
description The request sent by the client was syntactically incorrect ().
Apache Tomcat/6.0.26
I have tried to shorten the question as possible as I could and I haven't included unnecessary code.
How could be made it possible to upload multiple files after Spring has been configured with HiddenHttpMethodFilter
?
That blog indicates that It is a long standing, high priority bug.
If there is no solution regarding the version 3.0.2 (3 or higher) then I have to disable Spring support forever and continue to use commons-fileupolad as suggested by the third solution on that blog omitting the PUT, DELETE and other request methods forever.
Very little changes to the code in the parseFileItems()
method inside the class MultiCommonsMultipartResolver
might make it upload multiple files but I couldn't succeed in my attempts (again with the Spring version 3.0.2 (3 or higher)).
For upload multiple files in one request I used this code:
I have such jsp:
<p>Select files to upload. Press Add button to add more file inputs.</p>
<table>
<tr>
<td><input name="files" type="file" multiple="true"/></td>
</tr>
<tr>
<td><input name="files" type="file" multiple="true"/></td>
</tr>
</table>
<br/><input type="submit" value="Upload" />
File upload class bean:
import org.springframework.web.multipart.commons.CommonsMultipartFile;
public class FileUploadForm {
private CommonsMultipartFile [] files;
public CommonsMultipartFile[] getFiles() {
return files;
}
public void setFiles( CommonsMultipartFile[] files ) {
this.files = files;
}
}
Controller:
@Controller
@RequestMapping("/upload")
public class FileUploadController {
@RequestMapping(method = RequestMethod.GET)
public String displayForm(ModelMap modelMap) {
modelMap.addAttribute( new FileUploadForm() );
return "uploadForm.jsp";
}
@RequestMapping(method = RequestMethod.POST)
public String save(FileUploadForm uploadForm) {
CommonsMultipartFile[] files = uploadForm.getFiles();
if(files != null && files.length != 0) {
for(MultipartFile file : files) {
System.out.println( file.getOriginalFilename() );
}
}
return "success.jsp";
}
}
This code allows to upload multiple files in one request,
and be possible to get instance of CommonsMultipartFile
for each file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With