Exemple de téléchargement de fichier Spring Boot - Ajax et REST
Cet article vous montre comment télécharger des fichiers dans l'application Web Spring Boot (structure REST), à l'aide de requêtes Ajax.
Outils utilisés dans cet article:
-
Spring Boot 1.4.3.RELEASE
-
Spring 4.3.5.RELEASE
-
Thymeleaf
-
jQuery (webjars)
-
Maven
-
Tomcat 8.5.6 intégré
-
Navigateur Google Chrome (Network Inspect)
1. Structure du projet
Une structure de projet Maven standard.
2. Dépendance du projet
Déclare une dépendance webjarjQuery
supplémentaire, pour les requêtes Ajax au format HTML.
pom.xml
4.0.0 com.example spring-boot-file-upload jar 1.0 org.springframework.boot spring-boot-starter-parent 1.4.3.RELEASE 1.8 org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-devtools true org.webjars jquery 2.2.4 org.springframework.boot spring-boot-maven-plugin
3. Téléchargement de fichiers
Pour prendre en charge la requête et la réponse Ajax, la solution la plus simple est de renvoyer unResponseEntity
.
3.1 The below example demonstrates three possible ways to upload files:
-
Téléchargement de fichier unique -
MultipartFile
-
Téléchargement de plusieurs fichiers -
MultipartFile[]
-
Télécharger le fichier de mappage vers un modèle -
@ModelAttribute
RestUploadController.java
package com.example.controller; import com.example.model.UploadModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @RestController public class RestUploadController { private final Logger logger = LoggerFactory.getLogger(RestUploadController.class); //Save the uploaded file to this folder private static String UPLOADED_FOLDER = "F://temp//"; // 3.1.1 Single file upload @PostMapping("/api/upload") // If not @RestController, uncomment this //@ResponseBody public ResponseEntity> uploadFile( @RequestParam("file") MultipartFile uploadfile) { logger.debug("Single file upload!"); if (uploadfile.isEmpty()) { return new ResponseEntity("please select a file!", HttpStatus.OK); } try { saveUploadedFiles(Arrays.asList(uploadfile)); } catch (IOException e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } return new ResponseEntity("Successfully uploaded - " + uploadfile.getOriginalFilename(), new HttpHeaders(), HttpStatus.OK); } // 3.1.2 Multiple file upload @PostMapping("/api/upload/multi") public ResponseEntity> uploadFileMulti( @RequestParam("extraField") String extraField, @RequestParam("files") MultipartFile[] uploadfiles) { logger.debug("Multiple file upload!"); // Get file name String uploadedFileName = Arrays.stream(uploadfiles).map(x -> x.getOriginalFilename()) .filter(x -> !StringUtils.isEmpty(x)).collect(Collectors.joining(" , ")); if (StringUtils.isEmpty(uploadedFileName)) { return new ResponseEntity("please select a file!", HttpStatus.OK); } try { saveUploadedFiles(Arrays.asList(uploadfiles)); } catch (IOException e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } return new ResponseEntity("Successfully uploaded - " + uploadedFileName, HttpStatus.OK); } // 3.1.3 maps html form to a Model @PostMapping("/api/upload/multi/model") public ResponseEntity> multiUploadFileModel(@ModelAttribute UploadModel model) { logger.debug("Multiple file upload! With UploadModel"); try { saveUploadedFiles(Arrays.asList(model.getFiles())); } catch (IOException e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } return new ResponseEntity("Successfully uploaded!", HttpStatus.OK); } //save file private void saveUploadedFiles(Listfiles) throws IOException { for (MultipartFile file : files) { if (file.isEmpty()) { continue; //next pls } byte[] bytes = file.getBytes(); Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename()); Files.write(path, bytes); } } }
3.2 A simple model for above example 3.1.3 – @ModelAttribute
UploadModel.java
package com.example.model; import org.springframework.web.multipart.MultipartFile; public class UploadModel { private String extraField; private MultipartFile[] files; //getters and setters }
4. Les vues
Formulaire HTML pour les téléchargements de fichiers multiples.
upload.html
Spring Boot - Multiple file upload example - AJAX
Ajax Post Result
5. jQuery - Requête Ajax
jQuery pour obtenir le formulaire via le formulaire#id
, et envoyer les données du formulaire en plusieurs parties via la requête Ajax.
resources/static/js/main.js
$(document).ready(function () { $("#btnSubmit").click(function (event) { //stop submit the form, we will post it manually. event.preventDefault(); fire_ajax_submit(); }); }); function fire_ajax_submit() { // Get form var form = $('#fileUploadForm')[0]; var data = new FormData(form); data.append("CustomField", "This is some extra data, testing"); $("#btnSubmit").prop("disabled", true); $.ajax({ type: "POST", enctype: 'multipart/form-data', url: "/api/upload/multi", data: data, //http://api.jquery.com/jQuery.ajax/ //https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects processData: false, //prevent jQuery from automatically transforming the data into a query string contentType: false, cache: false, timeout: 600000, success: function (data) { $("#result").text(data); console.log("SUCCESS : ", data); $("#btnSubmit").prop("disabled", false); }, error: function (e) { $("#result").text(e.responseText); console.log("ERROR : ", e); $("#btnSubmit").prop("disabled", false); } }); }
6. Gestionnaire d'exceptions
Pour gérer l'exception de la requête Ajax, étend simplementResponseEntityExceptionHandler
et renvoie unResponseEntity
.
RestGlobalExceptionHandler.java
package com.example.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartException; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import javax.servlet.http.HttpServletRequest; //http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-error-handling @ControllerAdvice public class RestGlobalExceptionHandler extends ResponseEntityExceptionHandler { // Catch file size exceeded exception! @ExceptionHandler(MultipartException.class) @ResponseBody ResponseEntity> handleControllerException(HttpServletRequest request, Throwable ex) { HttpStatus status = getStatus(request); return new ResponseEntity(ex.getMessage(), status); // example //return new ResponseEntity("success", responseHeaders, HttpStatus.OK); } private HttpStatus getStatus(HttpServletRequest request) { Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); if (statusCode == null) { return HttpStatus.INTERNAL_SERVER_ERROR; } return HttpStatus.valueOf(statusCode); } }
7. DEMO
Démarrez Spring Boot avec les Tomcatmvn spring-boot:run
intégrés par défaut.
7.1 Access http://localhost:8080/, select few files and clicks submit to fire the ajax request.
7.2 Google Chrome, review the request and response in the “Network Inspect”
7.3 Google Chrome, “Request Payload”
8. Test cURL
Plus de tests avec la commandecURL
.
8.1 Test single file upload.
Terminal
$ curl -F file=@"f:\\data.txt" http://localhost:8080/api/upload/ Successfully uploaded - data.txt
8.2 Test multiple file upload.
Terminal
$ curl -F extraField="abc" -F files=@"f://data.txt" -F files=@"f://data2.txt" http://localhost:8080/api/upload/multi/ Successfully uploaded - data.txt , data2.txt
8.3 Test multiple file upload, maps to Model.
Terminal
$ curl -F extraField="abc" -F files=@"f://data.txt" -F files=@"f://data2.txt" http://localhost:8080/api/upload/multi/model Successfully uploaded!
8.4 Test a large movie file (100MB), the following error message will be displayed.
Terminal
$ curl -F file=@"F://movies//300//Sample.mkv" http://localhost:8080/api/upload/ Attachment size exceeds the allowable limit! (10MB)
9. Test cURL + objet d'erreur personnalisé
9.1 Create an object to store the error detail.
CustomError.java
package com.example.exception; public class CustomError { String errCode; String errDesc; public CustomError(String errCode, String errDesc) { this.errCode = errCode; this.errDesc = errDesc; } //getters and setters }
9.2 Update the global exception handler to support CustomError
object.
RestGlobalExceptionHandler.java
package com.example.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartException; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import javax.servlet.http.HttpServletRequest; @ControllerAdvice public class RestGlobalExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(MultipartException.class) @ResponseBody ResponseEntity> handleControllerException(HttpServletRequest request, Throwable ex) { HttpStatus status = getStatus(request); return new ResponseEntity(new CustomError("0x000123", "Attachment size exceeds the allowable limit! (10MB)"), status); //return new ResponseEntity("Attachment size exceeds the allowable limit! (10MB)", status); } private HttpStatus getStatus(HttpServletRequest request) { Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); if (statusCode == null) { return HttpStatus.INTERNAL_SERVER_ERROR; } return HttpStatus.valueOf(statusCode); } }
9.3 cURL to upload a large file again.
Terminal
$ curl -F file=@"F://movies//300//Sample.mkv" http://localhost:8080/api/upload/ {"errCode":"0x000123","errDesc":"Attachment size exceeds the allowable limit! (10MB)"}
Terminé. Vos commentaires sont les bienvenus.
10. Télécharger le code source
Télécharger -spring-boot-file-upload-ajax-rest.zip (11 KB)