架构师

您现在的位置是:首页 > 程序人生 > 程序人生

程序人生

Spring Boot文件上传示例(Ajax和REST)

2019-07-17 程序人生
本文介绍如何使用Ajax请求在Spring Boot Web应用程序(REST结构)中上传文件。本文中使用的工具: Spring Boot 1.4.3.RELEASE Spring 4.3.5.RELEASE Thymeleaf jQuery (webjars
Spring Boot文件上传示例(Ajax和REST)(五十二)

本文介绍如何使用Ajax请求在Spring Boot Web应用程序(REST结构)中上传文件。

本文中使用的工具:

Spring Boot 1.4.3.RELEASE Spring 4.3.5.RELEASE Thymeleaf jQuery (webjars) Maven Embedded Tomcat 8.5.6 Google Chrome浏览器

1. 项目结构

一个标准的Maven项目结构。如下图所示 -

Spring Boot文件上传示例(Ajax和REST)(五十二)

2. 项目依赖

声明一个额外的jQuery webjar依赖关系,适用于HTML格式的Ajax请求。

文件:pom.xml

```xml

4.0.0

<groupId>com.felix</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> ## 3.文件上传 为了支持Ajax请求和响应,最简单的解决方案返回一个ResponseEntity。 以下示例演示了上传文件的三种可能方式: 1. 单文件上传 - `MultipartFile` 2. 多文件上传 - `MultipartFile []` 3. 将文件上传到模型 - `@ModelAttribute` *文件:RestUploadController.java* ```java package com.felix.controller; import com.felix.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 = "D://temp//"; //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); } // Multiple file upload @PostMapping("/api/upload/multi") public ResponseEntity<?> uploadFileMulti( @RequestParam("extraField") String extraField, @RequestParam("files") MultipartFile[] uploadfiles) { logger.debug("Multiple file upload!"); 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); } // 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); } } }

以上示例的简单模型 - @ModelAttribute,文件:UploadModel.java -

package com.felix.model; import org.springframework.web.multipart.MultipartFile; import java.util.Arrays; public class UploadModel { private String extraField; private MultipartFile[] files; public String getExtraField() { return extraField; } public void setExtraField(String extraField) { this.extraField = extraField; } public MultipartFile[] getFiles() { return files; } public void setFiles(MultipartFile[] files) { this.files = files; } @Override public String toString() { return "UploadModel{" + "extraField='" + extraField + '\'' + ", files=" + Arrays.toString(files) + '}'; } }

视图文件

多个文件上传的HTML表单。文件:upload.html -

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<body>

<h2>Spring Boot多文件上传示例(使用AJAX)</h2>

<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="提交" id="btnSubmit"/>

</form>

<h1>Ajax提交结果:</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通过表单#id获取表单,并通过Ajax请求发送多部分(multipart)表单数据。文件: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/

//http://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.felix.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 { @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); // example //return new ResponseEntity(ex.getMessage(), status); //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. 运行演示

使用默认的嵌入式Tomcat的启动Spring Boot命令:mvn spring-boot:run.。

访问:http://localhost:8080/ ,选择几个文件并单击提交以触发ajax请求。

Spring Boot文件上传示例(Ajax和REST)(五十二)

提示:打开目录:F:/temp 应该能看到上传的文件。

文章评论