Les formulaires qui ne pourraient échanger que les données textuelles ne trouveraient pas leur utilité dans le monde des applications. C'est pourquoi nous allons aborder maintenant une partie consacrée au formulaire qui permet de transférer les fichiers.
Upload des fichiers sous Spring
Cependant, il n'y a que les valeurs textuelles qu'on peut envoyer via les formulaires dans Spring. Il y a aussi les fichiers qui peuvent être traités par les formulaires. Tout cela grâce au bean qui s'appelle multipartResolver. Au titre de rappel, on voici sa définition :
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="100000"/> <property name="maxInMemorySize" value="100000"/> <property name="defaultEncoding" value="utf-8"/> </bean>
Dès que multipartResolver va détecter que la requête contient des fichiers, il va le signaler au DispatcherServlet. Ce dernier se chargera alors de rediriger la tâche à l'implémentation de l'interface HttpServletRequest adaptée à ce type de traitement. Ensuite dans le contrôleur on peut la caster en MultipartHttpServletRequest pour récupérer les fichiers transférés. Ces fichiers se cachent sous instances de la classe MultipartFile.
Cependant, dans notre exemple on utilisera une approche légèrement différente. Il s'agira du transfert d'un avatar. Il sera attaché à l'entité Subscriber qui contiendra un attribut annoté avec @Transient, comme ci-dessous :
public class Subscriber extends ParentEntity implements Serializable { // other code omitted @Transient public CommonsMultipartFile getAvatarFile() { return avatarFile; } }
L'avatar sera l'instance de la classe org.springframework.web.multipart.commons.CommonsMultipartFile. Elle implémente l'interface évoquée ci-dessus, MultipartFile. Regardons comment intercepter cet objet dans le contrôleur :
@Controller public class SubscriberController extends FrontendController { // only this method interests us @RequestMapping(value = "/account/avatar", method = RequestMethod.POST) public String modifyAvatarHandle(@ModelAttribute("subscriber") @Validated({SubscriberAvatarCheck.class}) Subscriber subscriber, BindingResult binRes, @LoggedUser AuthenticationFrontendUserDetails user, Model layout, RedirectAttributes redAtt) { logger.info("Received POST request " + subscriber); if (binRes.hasErrors()) { redAtt.addFlashAttribute("error", true); redAtt.addFlashAttribute("subscriber", subscriber); redAtt.addFlashAttribute("errors", binRes); } else { try { Subscriber subFromUser = conversionService.convert(user, Subscriber.class); subFromUser.setAvatarFile(subscriber.getAvatarFile()); subscriberService.addAvatar(subFromUser); redAtt.addFlashAttribute("success", true); } catch (Exception e) { binRes.addError(getExceptionError("subscriber")); redAtt.addFlashAttribute("error", true); redAtt.addFlashAttribute("subscriber", subscriber); redAtt.addFlashAttribute("errors", binRes); } } return "redirect:/account/avatar"; } }
La récupération de l'image rajoutée se fait donc sans aucun souci, via le getter adéquat. Ensuite l'instance MultipartFile est passée au service qui s'occupe de transférer l'image physiquement sur le serveur ainsi que marquer ce fait dans la base de données :
public class SubscriberServiceImpl implements SubscriberService { @Override public Subscriber addAvatar(Subscriber subscriber) throws Exception { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { Map<String, Object> uploadResult = imageTool.uploadFile("avatar", subscriber.getAvatarFile(), subscriber.getLogin()); if (((Boolean)uploadResult.get("uploadResult")) == true) { subscriber.setAvatar((String)uploadResult.get("fileBasename")); subscriber = subscriberRepository.save(subscriber); transactionManager.commit(status); } else { subscriber = null; } } catch(Exception e) { logger.error("An error occured on adding subscriber avatar", e); transactionManager.rollback(status); subscriber = null; throw new Exception(e); } return subscriber; } }
Dans la transaction on tente d'abord de transférer l'instance MultipartFile sur le serveur. Si le résultat d'upload est positif, on récupère le nom du fichier envoyé et on met à jour le champ avatar de l'entité Subscriber. Après on sauvegarde les modifications dans la base de données. Actuellement on ne va pas entrer dans les détails concernant le transfert de l'image sur le serveur. Ce sera le sujet de l'article consacré au transfert des images dans Spring.