I'm new to spring, and i'm currently struggling with the many pieces required to get a multipart form submit/validation scenario with error beeing displayed in the view.
Here are the files i currently have :
resourceupload.jsp : a view that displays a form to upload the file.
<form:form method="post" action="resource/upload" enctype="mutlipart/form-data">
<input name="name" type="text"/>
<input name="file" type="file" />
<input type="submit"/>
<form:errors path="file" cssClass="errors"/>
</form>
resourceuploadcontroller.java : the controller that handles the form submit, and (unsuccessfuly) tries to post file validation errors back to the view :
@RequestMapping(method = RequestMethod.POST)
public String handleFormUpload( @RequestParam("file") MultipartFile file , @RequestParam("name") String name,Object command, Errors validationErrors){
..perform some stuff with the file content, checking things in the database, etc...
.. calling validationErrors.reject("file","the error") everytime something goes wrong...
return "redirect:upload"; // redirect to the form, that should display the error messages
Now, obviously there's something wrong with this approach:
1/ I had to add a dummy "command" object before the validationErrors parameter, otherwise spring would throw me an error. That doesn't seem really right.
2/ After I added that parameter, the redirect doesn't pass the errors to the view. I tried using @SessionAttribute("file") at the start of the controller, without any luck.
If anyone could help... I've had a look at @ResponseBody annotation, but that doesn't seem to be made to be used with views..
Seems like I found the solution on my own.
First, the link that helped me a lot : http://www.ioncannon.net/programming/975/spring-3-file-upload-example/ and Spring 3 MVC - form:errors not showing the errors which showed a good trick to display all errors, using
<form:errors path="*"/>.
Now, a list of all the things i changed to make that thing work :
1/ use "rejectValue" instead of "reject".
2/ return the view directly instead of a redirect.
3/ create a "UploadItem" model with a CommonsMultipartFile property
So, all in all, my controller method became
@RequestMapping(method = RequestMethod.POST)
public String handleFormUpload( @ModelAttribute("uploadItem") UploadItem uploadItem, BindingResult errors){
... use errors.rejectValue ... in case of errors (moving everything i could in a UploadItemValidator.validate function)
return "uploadform"
Hope that helped.
Here is a very easy way to do it:
The formBackingObject:
import org.springframework.web.multipart.commons.CommonsMultipartFile;
public class FileImport {
CommonsMultipartFile importFile;
public CommonsMultipartFile getImportFile() {
return importFile;
}
public void setImportFile(CommonsMultipartFile importFile) {
this.importFile = importFile;
}
}
The form:
<sf:form method="POST" modelAttribute="fileImport" enctype="multipart/form-data" action="import">
<table>
<spring:hasBindErrors name="fileImport">
<tr>
<td colspan="2">
<sf:errors path="importFile" cssClass="error"/>
</td>
</tr>
</spring:hasBindErrors>
<tr>
<th><label for="importFile">Import Workload:</label></th>
<td><input name="importFile" type="file"></td>
</tr>
</table>
<input type="submit" value="Import Application Data">
</sf:form>
And finally the ControllerClassMethod:
@RequestMapping(value={"/import"}, method=RequestMethod.POST)
public String importWorkload(
FileImport fileImport, BindingResult bindResult,
@RequestParam(value="importFile", required=true) MultipartFile importFile ){
if( 0 == importFile.getSize() ){
bindResult.rejectValue("importFile","fileImport.importFile");
}
if(bindResult.hasErrors()){
return "admin";
}
....
}
Easy Peasy!
The reason the @NotNull annotation does not work for files is because the multipart file is never null in the request object. But, it can have 0 bytes. You can take the time to implement a custom validator, but why?
In Spring 4, you can implement the file validator like below:
@Component
public class FileValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return FileModel.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
FileModel fileModel = (FileModel) target;
if (fileModel.getFile() != null && fileModel.getFile().isEmpty()){
errors.rejectValue("file", "file.empty");
}
}
}
And then inject above validator into the upload controller such as below:
@Controller
@RequestMapping("/")
public class FileUploadController {
@Autowired
private FileValidator fileValidator;
@ModelAttribute
public FileModel fileModel(){
return new FileModel();
}
@InitBinder
protected void initBinderFileModel(WebDataBinder binder) {
binder.setValidator(fileValidator);
}
@RequestMapping(method = RequestMethod.GET)
public String single(){
return "index";
}
@RequestMapping(method = RequestMethod.POST)
public String handleFormUpload(@Valid FileModel fileModel,
BindingResult result,
RedirectAttributes redirectMap) throws IOException {
if (result.hasErrors()){
return "index";
}
MultipartFile file = fileModel.getFile();
InputStream in = file.getInputStream();
File destination = new File("/tmp/" + file.getOriginalFilename());
FileUtils.copyInputStreamToFile(in, destination);
redirectMap.addFlashAttribute("filename", file.getOriginalFilename());
return "redirect:success";
}
}
This implementation helps your code more clear and more readable. I found it from the tutorial Spring MVC File Upload Validation Example
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