Springブートファイルのアップロードの例 – AjaxとREST
この記事では、Ajaxリクエストを使用して、SpringブートWebアプリケーション(REST構造)にファイルをアップロードする方法を説明します。
この記事で使用されているツール:
-
春のブート1.4.3.RELEASE
-
Spring 4.3.5.RELEASE
-
タイメレフ
-
jQuery(webjars)
-
Maven
-
埋め込みTomcat 8.5.6
-
Google Chromeブラウザ(ネットワーク検査)
1.プロジェクトの構成
標準のMavenプロジェクト構造。

プロジェクトの依存関係
HTML形式のAjaxリクエストに対して、追加の
jQuery
Webjar依存関係を宣言します。
pom.xml
<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/maven-v4__0__0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong</groupId>
<artifactId>spring-boot-file-upload</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- hot swapping, disable cache for template, enable live reload -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>2.2.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Package as an executable jar/war -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.ファイルアップロード
Ajaxの要求と応答をサポートするために、最も簡単な解決法が `ResponseEntity`を返します。
3.1以下の例は、ファイルをアップロードする3つの方法を示しています。
-
単一ファイルアップロード –
MultipartFile
-
複数ファイルのアップロード –
MultipartFile[]
-
ファイルをモデルにマップする –
@ ModelAttribute
RestUploadController.java
package com.mkyong.controller;
import com.mkyong.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(List<MultipartFile> files) 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上の例の単純なモデル3.1.3 –
@ ModelAttribute
UploadModel.java
package com.mkyong.model;
import org.springframework.web.multipart.MultipartFile;
public class UploadModel {
private String extraField;
private MultipartFile[]files;
//getters and setters
}
4.ビュー
複数のファイルをアップロードするためのHTMLフォーム。
upload.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h1>Spring Boot - Multiple file upload example - AJAX</h1>
<form method="POST" enctype="multipart/form-data" id="fileUploadForm">
<input type="text" name="extraField"/><br/><br/>
<input type="file" name="files"/><br/><br/>
<input type="file" name="files"/><br/><br/>
<input type="submit" value="Submit" id="btnSubmit"/>
</form>
<h1>Ajax Post Result</h1>
<pre>
<span id="result"></span>
</pre>
<script type="text/javascript"
src="webjars/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
5. jQuery – Ajaxリクエスト
jQueryを使用してフォームを取得し、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.mkyong.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);
}
}
デモ
デフォルトの埋め込みTomcat `mvn spring-boot:run`でSpringブートを開始します。
7.1
http://localhost:8080/
にアクセスし、いくつかのファイルを選択し、submitをクリックしてajaxリクエストを発行します。

7.2 Google Chromeでは、「ネットワーク検査」でリクエストと応答を確認します。

7.3 Google Chrome、「リクエストペイロード」

8. cURLテスト
`cURL`コマンドでより多くのテスト。
8.1単一ファイルのアップロードをテストします。
ターミナル
$ curl -F file=@"f:\\data.txt" http://localhost:8080/api/upload/Successfully uploaded - data.txt
8.2複数ファイルのアップロードをテストする。
ターミナル
$ 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複数ファイルのアップロードをテストし、モデルにマップします。
ターミナル
$ 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大きなムービーファイル(100MB)をテストすると、次のエラーメッセージが表示されます。
ターミナル
$ curl -F file=@"F://movies//300//Sample.mkv" http://localhost:8080/api/upload/Attachment size exceeds the allowable limit! (10MB)
9. cURLテスト+カスタムエラーオブジェクト
9.1エラーの詳細を格納するオブジェクトを作成します。
CustomError.java
package com.mkyong.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グローバル例外ハンドラを更新して、 `CustomError`オブジェクトをサポートします。
RestGlobalExceptionHandler.java
package com.mkyong.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を実行して大きなファイルを再度アップロードします。
ターミナル
$ 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)
参考文献
ブート – エラー処理]。 link://spring-boot/spring-boot-file-upload-example/[Springブートファイル
アップロードの例]。 link://spring-mvc/spring-mvc-file-upload-example/[Spring MVCファイル
アップロードの例]。リンク://spring-boot/spring-boot-hello-world-example-thymeleaf/[Spring
ブートHello Worldの例 – Thymeleaf]。
Wikipedia – cURL
-
https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using
FormData
Objects[MDN
ajax
curl
リンク://タグ/ファイルアップロード/[ファイルアップロード]リンク://タグ/jquery/[jquery]
multipart
rest
spring boot