Spring Bootファイルのアップロード例– AjaxとREST
この記事では、Ajaxリクエストを使用して、Spring Boot Webアプリケーション(REST構造)でファイルをアップロードする方法を示します。
この記事で使用されるツール:
-
Spring Boot 1.4.3.RELEASE
-
Spring 4.3.5.RELEASE
-
タイムリーフ
-
jQuery(webjars)
-
メーベン
-
組み込みTomcat 8.5.6
-
Google Chromeブラウザ(ネットワーク検査)
1. プロジェクト構造
標準のMavenプロジェクト構造。
2. プロジェクトの依存関係
HTML形式のAjaxリクエストに対して、追加のjQuery
webjar依存関係を宣言します。
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. ファイルアップロード
Ajaxのリクエストとレスポンスをサポートするために、最も簡単な解決策はResponseEntity
で返されます。
3.1 The below example demonstrates three possible ways to upload files:
-
単一ファイルのアップロード–
MultipartFile
-
複数ファイルのアップロード -
MultipartFile[]
-
モデルへのマップファイルのアップロード–
@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. ビュー
複数のファイルをアップロードするためのHTMLフォーム。
upload.html
Spring Boot - Multiple file upload example - AJAX
Ajax Post Result
5. jQuery – Ajaxリクエスト
jQueryを使用して、フォーム#id
を介してフォームを取得し、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. 例外ハンドラー
Ajaxリクエストからの例外を処理するには、ResponseEntityExceptionHandler
を拡張し、ResponseEntity
を返します。
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
デフォルトの組み込みTomcatmvn spring-boot:run
を使用してSpringBootを起動します。
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. cURLテスト
cURL
コマンドを使用したその他のテスト。
8.1 Test single file upload.
ターミナル
$ curl -F file=@"f:\\data.txt" http://localhost:8080/api/upload/ Successfully uploaded - data.txt
8.2 Test multiple file upload.
ターミナル
$ 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.
ターミナル
$ 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.
ターミナル
$ curl -F file=@"F://movies//300//Sample.mkv" http://localhost:8080/api/upload/ Attachment size exceeds the allowable limit! (10MB)
9. cURLテスト+カスタムエラーオブジェクト
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.
ターミナル
$ curl -F file=@"F://movies//300//Sample.mkv" http://localhost:8080/api/upload/ {"errCode":"0x000123","errDesc":"Attachment size exceeds the allowable limit! (10MB)"}
完了しました。 フィードバックは大歓迎です。
10. ソースコードをダウンロード
ダウンロード–spring-boot-file-upload-ajax-rest.zip(11 KB)