今天總結下Spring中的檔案上傳與下載,在寫這篇部落格之前,小編寫過一篇有關檔案上傳下載的博文------React Native 上傳圖片至七牛雲端儲存,該博文主要聊到採用React Native 框架如何將檔案上傳到七牛雲端儲存上,後端部分主要整合了七牛雲提供的API,但並沒有從底層講到檔案上傳下載的基本原理,所以本篇博文儘量彌補之前的不足,接下來,我們一起開始吧!
一、Java 檔案上傳下載的基本原理
就Java web 而言,對於檔案上傳,前端瀏覽器提交一個檔案,然後後端處理返回檔案上傳成功或者失敗的結果;對於檔案下載,很多情況就是前端單擊某個按鈕或者點選某個文字連結,然後瀏覽器就會自動將檔案上傳到本地中。那麼這其中的原理是什麼呢?小編認為,檔案的上傳下載本質上是通過Java 提供的流API來處理的,在Servlet中我們可以使用Http相關的HttpServletResponse
物件將檔案流輸出到瀏覽器,當然這涉及到我們瀏覽器如何識別該流是一個檔案還是一個文字字串的問題,所以檔案上傳下載還提到了Http請求頭Mime型別的設定.
小編以前會想這麼一個問題,我現在會使用html表單來上傳一個檔案,但當我使用一個框架的時候,我就不太明白其中的原理了。關於這個問題,小編認為原理都是一樣的,最本質的,都是通過表單來提交檔案上傳請求,也就是html中的<input type="file"/>
標籤,無論你框架怎麼封裝,底層就是一個表單。下面我們講下如何Spring boot 來實現檔案上傳下載,本示例前端部分採用簡單的html表單,沒有用到任何框架,簡單易懂。
二、Spring 對檔案上傳下載的支援
在Spring 框架中,對於檔案上傳下載的支援是通過MultipartFile
介面來實現的,一個MultipartFile
就表示客戶端傳過來的一個檔案,而一個MultipartFile[]
陣列,則表示客戶端傳過來的多個檔案,這在實現多檔案上傳非常有用,當然了,通過該介面,你可以獲取客戶端傳過來的檔案的一些基本資訊,比如檔名稱、檔案大小等.當然你也可以讓Spring限制客戶端傳過來檔案的大小和型別(比如圖片檔案png
還是文字檔案txt
),但這些功能的實現配置不在本文的範圍內。
Spring MVC傳統的REST風格用法是通過@Controller
、@ResponseBody
和@RequestMapping
三個註解實現的,在Spring Boot 中,你只需要使用@RestController
和@RequestMapping
兩個註解即可,簡單示例如下:
/**
* 測試檔案上傳與下載
*
* @author hjw
*
*/
@RestController
public class FileUploadCotroller {
@RequestMapping(value = "/upload.json", method = RequestMethod.POST)
public boolean fileUpload( MultipartFile file) throws IllegalStateException, IOException {
return false;
}
}
複製程式碼
由於Spring框架依賴了commons-fileupload
包,因此,我們需要在Maven的pom.xml
檔案中將該jar包引進來。
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
複製程式碼
三、採用Spring Boot 實現單檔案的上傳下載
1、檔案上傳
upload.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="http://localhost:8081/upload.json" method="POST" enctype="multipart/form-data">
<input name="file" type="file"/>
<button type="submit">single submit</button>
</form>
</body>
</html>
複製程式碼
FileUploadCotroller.java
@RestController
public class FileUploadCotroller {
@RequestMapping(value = "/upload.json", method = RequestMethod.POST)
public boolean fileUpload( MultipartFile file) throws IllegalStateException, IOException {
if (file.getSize() == 0) {
return false;
}
System.err.println("檔案是否為空 : " + file.isEmpty());
System.err.println("檔案的大小為 :" + file.getSize());
System.err.println("檔案的媒體型別為 : " + file.getContentType());
System.err.println("檔案的名字: " + file.getName());
System.err.println("檔案的originName為: " + file.getOriginalFilename());
File newFile = new File("C:\\Users\\hjw\\Desktop\\" + file.getOriginalFilename());
file.transferTo(newFile);
return true;
}
}
複製程式碼
上面的html檔案表單要設定表單的enctype
屬性為enctype="multipart/form-data"
,並且<input/>
標籤的name
屬性值必須和後端介面的MultipartFile
引數名一致,也就是都是file
2、檔案下載
檔案下載前端很簡單,我們直接使用一個<a><a/>
標籤即可。
download.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="http://localhost:8081/download.json">single download</a>
</body>
</html>
複製程式碼
FileUploadCotroller.java
@RequestMapping(value="download.json")
public boolean download(HttpServletResponse res) throws IOException {
File file = new File("C:\\Users\\hjw\\Desktop\\design\\code\\project-1\\myapp\\src\\images\\seats\\bg1.png");
String fileName = "bg1.png";
res.setHeader("Content-Disposition", "attachment;filename=" + fileName);
byte[] buff = new byte[1024];
BufferedInputStream bis = null;
OutputStream os = null;
try {
os = res.getOutputStream();
bis = new BufferedInputStream(new FileInputStream(file));
int i = bis.read(buff);
while (i != -1) {
os.write(buff, 0, buff.length);
os.flush();
i = bis.read(buff);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("success");
return false;
}
}
複製程式碼
上面的檔案下載採用流的形式進行傳輸,注意,要實現檔案下載功能,我們必須在響應頭中設定Http Header
的Content-Disposition
引數,該引數會啟用瀏覽器的下載功能,並接受我們傳輸的檔案。
四、採用Spring Boot 實現多檔案上傳
上面提到多檔案上傳對應MultipartFile[]
陣列,因此我們只需改下後端的引數即可。
multiUpload.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="http://localhost:8081/multiFile.json" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple="multiple" value="請選擇多個檔案"/>
<button type="submit">multi submit second</button>
</form>
</body>
</html>
複製程式碼
FileUploadCotroller.java
@RequestMapping(value="multiFile.json",method=RequestMethod.POST)
public boolean multiFile(MultipartFile[] files) throws IllegalStateException, IOException {
if(files.length == 0) {
return false;
}
File file = null;
String path = "C:\\Users\\hjw\\Desktop\\";
for (int i = 0; i < files.length; i++) {
System.err.println("第" + i + "個檔案的大小為" + files[i].getSize());
System.err.println("第" + i + "個檔案是否為空" + files[i].isEmpty());
System.err.println("第" + i + "個檔案的媒體型別為" + files[i].getContentType());
System.err.println("第" + i + "個檔案的檔名為" + files[i].getName());
System.err.println("第" + i + "個檔案的原始檔名為" + files[i].getOriginalFilename());
file = new File(path + files[i].getOriginalFilename());
files[i].transferTo(file);
}
return false;
}
複製程式碼
注意我們要設定html檔案<input/>
標籤的multiple
屬性為multiple="multiple"
,該屬性表示支援檔案多選操作。同時,和單檔案上傳相同,我們的<input/>
標籤的name
屬性必須和後端的MultipartFile[]
引數名相同,即files
.
總結
這些例子很簡單,屬於入門級別,當然檔案上傳下載做的好並不是這麼簡單的,只不過我們日常專案並不是對檔案上傳下載功能要求很苛刻(比如迅雷),當然,如果你想做的高階一點,你可以使用Java提供的檔案隨機存取類RandomAccessFile
去實現斷點上傳和下載,斷點上傳下載的功能http也是有提供相應的支援的。
謝謝閱讀!