Пример Upload file c Spring Boot и AngularJS
View more Tutorials:
В данной статье я покажу вам как создать приложение Upload File исопльзуя Spring Boot и AngularJS, ниже является изображение для предварительного просмотра приложениея, которое вы выполним:

Оповестить на интерфейсе когда происходит ошибка загрузки:

Отобразить список загруженных файлов, и обработать скачивание когда пользователь кликает на ссылку (link).

На Eclipse создать проект Spring Boot:


pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.o7planning</groupId> <artifactId>SpringBootFileUploadAngularJS</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>SpringBootFileUploadAngularJS</name> <description>Spring Boot + File Upload + AngularJS</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
SpringBootFileUploadAngularJsApplication.java
package org.o7planning.sbfileupload; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootFileUploadAngularJsApplication { public static void main(String[] args) { SpringApplication.run(SpringBootFileUploadAngularJsApplication.class, args); } }

Класс UploadForm представляет данные формы HTML.

UploadForm.java
package org.o7planning.sbfileupload.form; import org.springframework.web.multipart.MultipartFile; public class UploadForm { private String description; private MultipartFile[] files; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public MultipartFile[] getFiles() { return files; } public void setFiles(MultipartFile[] files) { this.files = files; } }
MainController.java
package org.o7planning.sbfileupload.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class MainController { @GetMapping("/") public String index() { return "upload"; } }
Класс MainRESTController определяет REST API чтобы обрабатывать данные файла загруженного пользователем. Данный REST API будет вызван с помощью AngularJS (Смотрите в UploadFileCtrl.js).
MainRESTController.java
package org.o7planning.sbfileupload.restcontroller; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import org.o7planning.sbfileupload.form.UploadForm; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController public class MainRESTController { // Linux: /home/{user}/test // Windows: C:/Users/{user}/test private static String UPLOAD_DIR = System.getProperty("user.home") + "/test"; @PostMapping("/rest/uploadMultiFiles") public ResponseEntity<?> uploadFileMulti(@ModelAttribute UploadForm form) throws Exception { System.out.println("Description:" + form.getDescription()); String result = null; try { result = this.saveUploadedFiles(form.getFiles()); } // Here Catch IOException only. // Other Exceptions catch by RestGlobalExceptionHandler class. catch (IOException e) { e.printStackTrace(); return new ResponseEntity<>("Error: " + e.getMessage(), HttpStatus.BAD_REQUEST); } return new ResponseEntity<String>("Uploaded to: " + result, HttpStatus.OK); } // Save Files private String saveUploadedFiles(MultipartFile[] files) throws IOException { // Make sure directory exists! File uploadDir = new File(UPLOAD_DIR); uploadDir.mkdirs(); StringBuilder sb = new StringBuilder(); for (MultipartFile file : files) { if (file.isEmpty()) { continue; } String uploadFilePath = UPLOAD_DIR + "/" + file.getOriginalFilename(); byte[] bytes = file.getBytes(); Path path = Paths.get(uploadFilePath); Files.write(path, bytes); sb.append(uploadFilePath).append(", "); } return sb.toString(); } @GetMapping("/rest/getAllFiles") public List<String> getListFiles() { File uploadDir = new File(UPLOAD_DIR); File[] files = uploadDir.listFiles(); List<String> list = new ArrayList<String>(); for (File file : files) { list.add(file.getName()); } return list; } // @filename: abc.zip,.. @GetMapping("/rest/files/{filename:.+}") public ResponseEntity<Resource> getFile(@PathVariable String filename) throws MalformedURLException { File file = new File(UPLOAD_DIR + "/" + filename); if (!file.exists()) { throw new RuntimeException("File not found"); } Resource resource = new UrlResource(file.toURI()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"") .body(resource); } }
По умолчанию размер файла загруженного на Server не должен превышать 1MB. И если пользователь загружает несколько файлов одновременно, общий размер так же не должен превышать 1MB. Но вы можете конфигурировать, чтобы изменить данные параметры.
application.properties
spring.servlet.multipart.max-file-size=1MB spring.servlet.multipart.max-request-size=5MB spring.thymeleaf.cache=false
RestGlobalExceptionHandler это кастомизированный класс, расширенный из класса ResponseEntityExceptionHandler. В данном классе вы можете обрабатывать исключения выброшенные (throw) из методов REST. Это поможет вам обрабатывать исключения централизованно, вместо обрабатывания исключения в каждом методе REST.
RestGlobalExceptionHandler.java
package org.o7planning.sbfileupload.exceptionhandler; 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 { // Catch max file size Exception. @ExceptionHandler(MultipartException.class) @ResponseBody public ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) { HttpStatus status = this.getStatus(request); return new ResponseEntity<String>("(Message in RestGlobalExceptionHandler *): " + ex.getMessage(), status); } // Catch Other Exception @ExceptionHandler(Exception.class) @ResponseBody public ResponseEntity<?> handleControllerRootException(HttpServletRequest request, Throwable ex) { HttpStatus status = this.getStatus(request); return new ResponseEntity<String>("(Message in RestGlobalExceptionHandler **): " + ex.getMessage(), 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); } }

upload.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Spring Boot File Upload with AngularJS</title> <meta charset="utf-8" /> <!-- Check other AngularJS version at: --> <!-- https://code.angularjs.org/1.6.9/docs/misc/downloading --> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script> <script src="/js/MainApp.js"></script> <script src="/js/UploadFileCtrl.js"></script> <script src="/js/GetFilesCtrl.js"></script> </head> <body ng-app="MainApp"> <h2>Spring Boot File Upload with AngularJS</h2> <div ng-controller="UploadFileController"> <form> Description: <br/> <input type="text" name="description" ng-model="myForm.description" style="width:350px;"/> <br/><br/> File to upload (1): <input type="file" file-model="myForm.files[0]"/><br /> File to upload (2): <input type="file" file-model="myForm.files[1]"/><br /> File to upload (3): <input type="file" file-model="myForm.files[2]"/><br /> File to upload (4): <input type="file" file-model="myForm.files[3]"/><br /> File to upload (5): <input type="file" file-model="myForm.files[4]"/><br /> <button type="button" ng-click="doUploadFile()">Upload</button> </form> <h2>Upload Results:</h2> <div style="border:1px solid #ccc;padding: 5px;"> <span ng-bind="uploadResult"></span> </div> </div> <!-- Get Files --> <hr> <div ng-controller="GetFilesController"> <button type="button" ng-click="getAllFiles()">Get All Files</button> <ul> <li ng-repeat="file in allFiles"> <a href='/rest/files/{{file}}'>{{file}}</a> </li> </ul> </div> </body> </html>
В AngularJS, использование атрибута (attribute) ng-model поможет вам с двусторонней привязкой (2-way binding) между элемент Input у Form и Model, что значит если данные на Model меняются то интерфейс (элемент Input) будет обновлен, и наоборот, если пользователь меняет на интерфейсее (элемент Input), то Model будет обновлен.

К сожалению атрибут (attribute) ng-model не поддерживает двусторонню привязку между Model и Input[file], поэтому вам нужно определить directive (директива) с названием "fileModel" чтобы построить двустороннюю привязку между Model и Input[file]. Данная Directive определена в MainApp.js:
js/MainApp.js
// main app. var mainApp = angular.module('MainApp', []); // DIRECTIVE - FILE MODEL mainApp.directive('fileModel', ['$parse', function ($parse) { return { restrict: 'A', link: function(scope, element, attrs) { var model = $parse(attrs.fileModel); var modelSetter = model.assign; element.bind('change', function(){ scope.$apply(function(){ modelSetter(scope, element[0].files[0]); }); }); } }; }]);
Файл UploadFileCtrl.js содержит функции AngularJS управляющие загрузку файлов на Server.
js/UploadFileCtrl.js
// CONTROLLER UPLOAD FILE mainApp.controller('UploadFileController', function($scope, $http) { $scope.uploadResult =""; $scope.myForm = { description: "", files: [] } $scope.doUploadFile = function() { var url = "/rest/uploadMultiFiles"; var data = new FormData(); data.append("description", $scope.myForm.description); for (i = 0; i < $scope.myForm.files.length; i++) { data.append("files", $scope.myForm.files[i]); } var config = { transformRequest: angular.identity, transformResponse: angular.identity, headers: { 'Content-Type': undefined } } $http.post(url, data, config).then( // Success function(response) { $scope.uploadResult = response.data; }, // Error function(response) { $scope.uploadResult = response.data; }); }; });
Файл GetFilesCtrl.js содержит файл AngularJS, управляющий получение списка файлов, загруженных на Server.
js/GetFilesCtrl.js
mainApp.controller('GetFilesController', function($scope, $http) { $scope.allFiles = []; $scope.getAllFiles = function() { // REST URL: var url = "/rest/getAllFiles"; $http.get(url).then( // Success function(response) { alert("OK"); $scope.allFiles = response.data; }, // Error function(response) { alert("Error: " + response.data); } ); }; });