Downloading multiple files using Spring MVC 3.0.2 after enabling HiddenHttpMethodFilter

Important: This question is absolutely useless for any version of Spring above 3.0.4 , since the issue discussed in this thread has been fixed in this version for a long time and is no longer reproduced in future versions of Spring.


I am using Spring version 3.0.2. I need to upload multiple files using the multiple="multiple" attribute in a file browser, for example,

 <input type="file" id="myFile" name="myFile" multiple="multiple"/> 

(and not using multiple file browsers like the one indicated by this answer , it really works, I tried).

Although no version of Internet Explorer supports this approach, unless the appropriate jQuery plugin / widget is used, I don’t care what most other browsers do.

This works fine with file loading , but in addition to the RequestMethod.POST and RequestMethod.GET methods, I also want to use other request methods supported and suggested by Spring as RequestMethod.PUT and RequestMethod.DELETE in their respective places. For this, I configured Spring with a HiddenHttpMethodFilter , which is great for the question this indicates.

but it can only upload one file at a time , even if multiple files are selected in the file browser. In the Spring controller class, the method is displayed 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 type MultipartFile (it can always only have one file at a time).


I could find a strategy that is likely to work with multiple files on this blog . I carefully studied it.

The solution below in DECISION 2 - USING THE RAW REQUEST says:

If, however, the client insists on using the same form input name as β€œfiles []” or β€œfiles”, and then filling this name with several files, then a little hack is required, as follows . As noted above, Spring2.5 throws an exception if it detects the same input name of a file type of a type more than once. CommonsFileUploadSupport - the class that throws this exception is not final, and the method that throws this exception is protected in such a way that using miracles of inheritance and subclasses of one can just fix / change the logic a little as follows . Ive changed literally one word, representing one invocation method which allows us to have several files included in the same login form.

He is trying to override the method

 protected MultipartParsingResult parseFileItems(List fileItems, String encoding){} 

of the abstract CommonsFileUploadSupport class, extending the CommonsMultipartResolver class, for example,

 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 parseFileItems() method (return statement) i.e.

 return new MultipartParsingResult(multipartFiles, multipartParameters); 

causes a compile-time error because the first parameter multipartFiles is a Map type implemented by HashMap , but in fact it requires a parameter of type MultiValueMap<String, MultipartFile>

This is the constructor of the static class inside the CommonsFileUploadSupport abstract class,

 public abstract class CommonsFileUploadSupport { protected static class MultipartParsingResult { public MultipartParsingResult(MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams) {} } } 

The reason may be - this solution applies to Spring version 2.5, and I'm using Spring version 3.0.2, which may not be acceptable for this version.


I tried replacing Map with MultiValueMap various ways, such as shown in the next code segment,

 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 without success. I'm not sure how to replace Map with MultiValueMap , and even this may work. After that, the browser shows the Http response,

HTTP Status 400

enter status report

message

description The request sent by the client was syntactically incorrect ().

Apache Tomcat / 6.0.26


I tried to shorten the question as soon as possible, and I did not include the extra code.

How was it possible to upload multiple files after Spring was configured using HiddenHttpMethodFilter ?

This blog indicates that this is a long-term, high-priority error.

If there is no solution regarding version 3.0.2 (3 or higher), then I need to disable Spring support forever and continue to use commons-fileupolad , as suggested by the third solution on this blog, permanently losing PUT, DELETE and other query methods.


Very few code changes in the parseFileItems() method inside the MultiCommonsMultipartResolver class can lead to loading multiple files, but I could not succeed in my attempts (again with Spring version 3.0.2 (3 or higher)).

+7
source share
2 answers

The question mentioned in the question has been fixed since Spring 3.0.4. Thus, if you used this version or higher (yes, it is now 4.xx), you will no longer need to read this question / answer.

+1
source

To download multiple files in one request, I used this code:

I have a jsp like this:

 <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" /> 

Bean file upload class:

 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 you to upload multiple files in one request, and you can get an instance of CommonsMultipartFile for each file.

+3
source

All Articles