文件上傳
springboot可以直接使用 org.springframework.web.multipart.MultipartFile實現文件上傳功能。
1.創建form表單:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>hah</title>
</head>
<body>
<text>單文件上傳</text>
<form method="post" action="/singleUpload" enctype="multipart/form-data">
<input type="file" name="headerImg"><br>
<input type="submit" value="提交">
</form>
<text>多文件上傳</text>
<form method="post" action="/multiUpload" enctype="multipart/form-data">
<input type="file" name="photos"><br>
<input type="file" name="photos"><br>
<input type="file" name="photos"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
2.yaml配置:
spring:
servlet:
multipart:
enabled: true
file-size-threshold: 2KB
max-request-size: 215MB
3.官網對於上傳文件yaml配置參數的解釋:
## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.(單個文件的最大值)
spring.servlet.multipart.max-file-size=200MB
# Max Request Size(上傳文件總的最大值)
spring.servlet.multipart.max-request-size=215MB
## File Storage Properties
# All files uploaded through the REST API will be stored in this directory
file.upload-dir=/Users/callicoder/uploads
4.寫一個Controller驗證:
- 單文件上傳
- 多文件上傳
@RestController
@Slf4j
public class UploadController {
//上傳單個文件
@PostMapping("/singleUpload")
public String multiUpload(
@RequestPart("headerImg") MultipartFile headerImg) throws IOException {
log.info("上傳的信息:headerImg={}",
headerImg.getSize());
if (!headerImg.isEmpty()) {
//保存到文件服務器,OSS服務器
String originalFilename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("E:\\cache\\" + originalFilename));
}
return "singleUpload";
}
//上傳多個文件
@PostMapping("/multiUpload")
public String singleUpload(
@RequestPart("photos") MultipartFile[] photos) throws IOException {
log.info("上傳的信息:photos={}",
photos.length);
if (photos.length > 0) {
for (MultipartFile photo : photos) {
if (!photo.isEmpty()) {
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("E:\\cache\\" + originalFilename));
}
}
}
return "multiUpload";
}
}
異常處理
默認情況下,Spring Boot為兩種情況提供了不同的響應方式。
一種是瀏覽器客戶端請求一個不存在的頁面或服務端處理發生異常時,一般情況下瀏覽器默認發送的請求頭中Accept: text/html,所以Spring Boot默認會響應一個html文檔內容,稱作“Whitelabel Error Page”。另一種是使用Postman等調試工具發送請求一個不存在的url或服務端處理發生異常時,Spring Boot會返回類似如下的Json格式字符串信息。Spring Boot 默認提供了程序出錯的結果映射路徑/error。這個/error請求會在BasicErrorController中處理,其內部是通過判斷請求頭中的Accept的內容是否為text/html來區分請求是來自客戶端瀏覽器(瀏覽器通常默認自動發送請求頭內容Accept:text/html)還是客戶端接口的調用,以此來決定返回頁面視圖還是 JSON 消息內容。
自定義錯誤頁面
error.html
先從最簡單的開始,直接在/resources/templates
下面創建error.html就可以覆蓋默認的Whitelabel Error Page
的錯誤頁面,我項目用的是thymeleaf模板,對應的error.html代碼如下:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
動態error錯誤頁面
<p th:text="${error}"></p>
<p th:text="${status}"></p>
<p th:text="${message}"></p>
</body>
</html>
靜態錯誤頁面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
靜態404錯誤頁面
</body>
</html>
靜態頁面資源目錄下存放404.html,發生404錯誤時會訪問到404.html
動態錯誤頁面:
存放路徑/resources/templates/error下,比如/resources/templates/error/4xx.html,/resources/templates/error/404.html
比如創建/resources/templates/error/4xx.html,則發生4打頭的錯誤碼時都會訪問4xx.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
4xx動態error錯誤頁面
<p th:text="${error}"></p>
<p th:text="${status}"></p>
<p th:text="${message}"></p>
</body>
</html>
頁面優先級
- error.html會覆蓋默認的 whitelabel Error Page 錯誤提示
- 靜態錯誤頁面優先級別比error.html高
- 動態模板錯誤頁面優先級比靜態錯誤頁面高
重寫ErrorController
在靜態處理代碼分析的時候我們說到了項目啟動時候就會自動加載默認異常處理配置ErrorMvcAutoConfiguration,會默認加載BasicErrorController作為異常處理的控制器。那么我們想要自己定義處理邏輯的話,我們就直接覆蓋掉BasicErrorController,然后重寫一個就好了。
繼承ErrorPageRegistrar
ErrorPageRegistrar是一個錯誤頁面的注冊器,在ErrorMvcAutoConfiguration中我們依然可以找到對應的源碼代碼,它默認幫我們寫了一個ErrorPageCustomizer用於處理注冊的錯誤頁面,我們可以看到啟動時候,會默認先把/error頁面注冊進去。
@ControllerAdvice注解處理異常
Spring正好提供了一個非常方便的異常處理方案——控制器通知 (@ControllerAdvice 或 @RestControllerAdvice),它將所有控制器作為一 個切面,利用切面技術來實現。可以減少因忘記寫catch而出現錯誤的概率通過基於@ControllerAdvice或@RestContollerAdvice的注解可以對異常進行全局統一處理,默認對所有的Controller有效。如果要限定生效范圍,則可以使用@ControllerAdvice支持的限定范圍方式。
不一定需要組合來一起用,當我們需要在某個特定控制器里面處理特定異常時候,我們的@ExceptionHandler可以直接寫在controller中,這樣的話@ExceptionHandler就只能處理這個單個controller的異常。那有時候我們想全局處理所有的控制器的異常,於是就有了@ControllerAdvice,它會控制器增強,會應用到所有的controller上,這樣就實現了我們想要的全局異常處理。
@ControllerAdvice支持的限定范圍
- 按注解:@ControllerAdvice(annotations = RestController .class)。
- 按包名:@ControllerAdvice("org.example.controller")。
- 按類型:@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})。
示例1:處理全局異常Exception.class
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
String handleException(){
return "Exception";
}
}
方法 handleException() 就會處理所有 Controller 層拋出的 Exception 及其子類的異常。被 @ExceptionHandler 注解的方法的參數列表里,還可以聲明很多種類型的參數。
示例2:處理多種異常
訪問/exceptionMethod會拋出ArithmeticException,GlobalExceptionHandler對ArithmeticException和Exception都進行了處理,會優先調用handleArithmeticException。
@Slf4j
@RestController
public class ExceptionController {
@RequestMapping("/exceptionMethod")
public String exceptionMethod(Model model) throws Exception {
model.addAttribute("msg", "沒有拋出異常");
int num = 1/0; //a處
log.info(String.valueOf(num));
return "home";
}
}
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
String handleException(){
return "Exception";
}
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
String handleArithmeticException(){
return "ArithmeticException";
}
@ExceptionHandler(value={java.lang.NullPointerException.class})
ModelAndView nullPointerExceptionHandler(Exception e){
System.out.println("goto NullPointerExceptionPage");
ModelAndView mv=new ModelAndView();
mv.addObject("error",e.toString());
mv.setViewName("NullPointerExceptionPage");
return mv;
}
}