基于Spring實(shí)現(xiàn)文件上傳功能
本小節(jié)你將建立一個(gè)可以接受HTTP multi-part 文件的服務(wù)。
你將建立一個(gè)后臺(tái)服務(wù)來接收文件以及前臺(tái)頁面來上傳文件。
要利用servlet容器上傳文件,你要注冊一個(gè)MultipartConfigElement類,以往需要在web.xml 中配置<multipart-config>,
而在這里,你要感謝SpringBoot,一切都為你自動(dòng)配置好了。
1、新建一個(gè)文件上傳的Controller:
應(yīng)用已經(jīng)包含一些 存儲(chǔ)文件 和 從磁盤中加載文件 的類,他們在cn.tiny77.guide05這個(gè)包下。我們將會(huì)在FileUploadController中用到這些類。
package cn.tiny77.guide05;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
public class FileUploadController {
private final StorageService storageService;
@Autowired
public FileUploadController(StorageService storageService) {
this.storageService = storageService;
}
@GetMapping("/")
public String listUploadedFiles(Model model) throws IOException {
List<String> paths = storageService.loadAll().map(
path -> MvcUriComponentsBuilder.fromMethodName(FileUploadController.class,
"serveFile", path.getFileName().toString()).build().toString())
.collect(Collectors.toList());
model.addAttribute("files", paths);
return "uploadForm";
}
@GetMapping("/files/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> serveFile(@PathVariable String filename) {
Resource file = storageService.loadAsResource(filename);
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getFilename() + "\"").body(file);
}
@PostMapping("/")
public String handleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
storageService.store(file);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded " + file.getOriginalFilename() + "!");
return "redirect:/";
}
@ExceptionHandler(StorageFileNotFoundException.class)
public ResponseEntity<?> handleStorageFileNotFound(StorageFileNotFoundException exc) {
return ResponseEntity.notFound().build();
}
}
該類用@Controller注解,因此SpringMvc可以基于它設(shè)定相應(yīng)的路由。每一個(gè)@GetMapping和@PostMapping注解將綁定對應(yīng)的請求參數(shù)和請求類型到特定的方法。
GET / 通過StorageService 掃描文件列表并 將他們加載到 Thymeleaf 模板中。它通過MvcUriComponentsBuilder來生成資源文件的連接地址。
GET /files/{filename} 當(dāng)文件存在時(shí)候,將加載文件,并發(fā)送文件到瀏覽器端。通過設(shè)置返回頭"Content-Disposition"來實(shí)現(xiàn)文件的下載。
POST / 接受multi-part文件并將它交給StorageService保存起來。
你需要提供一個(gè)服務(wù)接口StorageService來幫助Controller操作存儲(chǔ)層。接口大致如下
package cn.tiny77.guide05;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Path;
import java.util.stream.Stream;
public interface StorageService {
void init();
void store(MultipartFile file);
Stream<Path> loadAll();
Path load(String filename);
Resource loadAsResource(String filename);
void deleteAll();
}
以下是接口實(shí)現(xiàn)類
package cn.tiny77.guide05;
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.nio.file.StandardCopyOption;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
@Service
public class FileSystemStorageService implements StorageService {
private final Path rootLocation;
@Autowired
public FileSystemStorageService(StorageProperties properties) {
this.rootLocation = Paths.get(properties.getLocation());
}
@Override
public void store(MultipartFile file) {
String filename = StringUtils.cleanPath(file.getOriginalFilename());
try {
if (file.isEmpty()) {
throw new StorageException("無法保存空文件 " + filename);
}
if (filename.contains("..")) {
// This is a security check
throw new StorageException(
"無權(quán)訪問該位置 "
+ filename);
}
Files.copy(file.getInputStream(), this.rootLocation.resolve(filename),
StandardCopyOption.REPLACE_EXISTING);
}
catch (IOException e) {
throw new StorageException("無法保存文件 " + filename, e);
}
}
@Override
public Stream<Path> loadAll() {
try {
return Files.walk(this.rootLocation, 1)
.filter(path -> !path.equals(this.rootLocation))
.map(path -> this.rootLocation.relativize(path));
}
catch (IOException e) {
throw new StorageException("讀取文件異常", e);
}
}
@Override
public Path load(String filename) {
return rootLocation.resolve(filename);
}
@Override
public Resource loadAsResource(String filename) {
try {
Path file = load(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
}
else {
throw new StorageFileNotFoundException(
"無法讀取文件: " + filename);
}
}
catch (MalformedURLException e) {
throw new StorageFileNotFoundException("無法讀取文件: " + filename, e);
}
}
@Override
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
@Override
public void init() {
try {
Files.createDirectories(rootLocation);
}
catch (IOException e) {
throw new StorageException("初始化存儲(chǔ)空間出錯(cuò)", e);
}
}
}
2、建立一個(gè)Html頁面
這里使用Thymeleaf模板
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
<div>
<form method="POST" enctype="multipart/form-data" action="/">
<table>
<tr><td>File to upload:</td><td><input type="file" name="file" /></td></tr>
<tr><td></td><td><input type="submit" value="Upload" /></td></tr>
</table>
</form>
</div>
<div>
<ul>
<li th:each="file : ${files}">
<a th:href="${file}" rel="external nofollow" th:text="${file}" />
</li>
</ul>
</div>
</body>
</html>
頁面主要分為三部分分
- 頂部展示SpringMvc傳過來的信息
- 一個(gè)提供用戶上傳文件的表單
- 一個(gè)后臺(tái)提供的文件列表
3、限制上傳文件的大小
在文件上傳的應(yīng)用中通常要設(shè)置文件大小的,想象一下后臺(tái)處理的文件如果是5GB,那得多糟糕!在SpringBoot中,我們可以通過屬性文件來控制。
新建一個(gè)application.properties,代碼如下:
spring.http.multipart.max-file-size=128KB #文件總大小不能超過128kb
spring.http.multipart.max-request-size=128KB #請求數(shù)據(jù)的大小不能超過128kb
4、應(yīng)用啟動(dòng)函數(shù)
package cn.tiny77.guide05;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableConfigurationProperties(StorageProperties.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
CommandLineRunner init(StorageService storageService) {
return (args) -> {
storageService.deleteAll();
storageService.init();
};
}
}
5、運(yùn)行結(jié)果

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- SpringMVC文件上傳 多文件上傳實(shí)例
- Spring實(shí)現(xiàn)文件上傳(示例代碼)
- 詳解SpringBoot文件上傳下載和多文件上傳(圖文)
- jquery.form.js框架實(shí)現(xiàn)文件上傳功能案例解析(springmvc)
- SpringMVC 文件上傳配置,多文件上傳,使用的MultipartFile的實(shí)例
- 使用jQuery.form.js/springmvc框架實(shí)現(xiàn)文件上傳功能
- MyBatis與SpringMVC相結(jié)合實(shí)現(xiàn)文件上傳、下載功能
- springMVC配置環(huán)境實(shí)現(xiàn)文件上傳和下載
- SpringMVC文件上傳的配置實(shí)例詳解
- Spring Boot實(shí)現(xiàn)文件上傳示例代碼
相關(guān)文章
Java深入學(xué)習(xí)圖形用戶界面GUI之創(chuàng)建窗體
圖形編程中,窗口是一個(gè)重要的概念,窗口其實(shí)是一個(gè)矩形框,應(yīng)用程序可以使用其從而達(dá)到輸出結(jié)果和接受用戶輸入的效果,學(xué)習(xí)了GUI就讓我們用它來創(chuàng)建一個(gè)窗體2022-05-05
SpringBoot中DTO/VO/Entity相互轉(zhuǎn)換詳解
在我們平時(shí)開發(fā)中,dto、vo、entity之間的相互轉(zhuǎn)換是很頻繁的操作,這篇文章就簡單記錄一下在平時(shí)開發(fā)中SpringBoot的轉(zhuǎn)換方法,希望對大家有所幫助2025-01-01
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(42)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-07-07
Java 詳細(xì)講解分治算法如何實(shí)現(xiàn)歸并排序
分治算法的基本思想是將一個(gè)規(guī)模為N的問題分解為K個(gè)規(guī)模較小的子問題,這些子問題相互獨(dú)立且與原問題性質(zhì)相同。求出子問題的解,就可得到原問題的解,本篇文章我們就用分治算法來實(shí)現(xiàn)歸并排序2022-04-04
Spring boot如何集成kaptcha并生成驗(yàn)證碼
這篇文章主要介紹了Spring boot如何集成kaptcha并生成驗(yàn)證碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
SpringMvc切換Json轉(zhuǎn)換工具的操作代碼
SpringBoot切換使用goolge的Gson作為SpringMvc的Json轉(zhuǎn)換工具,本文給大家講解SpringMvc切換Json轉(zhuǎn)換工具的操作代碼,感興趣的朋友一起看看吧2024-02-02
解決springboot與springcloud版本兼容問題(附版本兼容表)
在基于spring boot搭建spring cloud時(shí),創(chuàng)建eureka后啟動(dòng)服務(wù)發(fā)生報(bào)錯(cuò),本文給大家介紹了解決springboot與springcloud版本兼容問題的幾種方案,需要的朋友可以參考下2024-02-02

