SpringBoot實現文件上傳下載實時進度條功能(附源碼)
0. 引言
記得剛入行的時候,做了一個文件上傳的功能,因為上傳時間較久,為了用戶友好性,想要添加一個實時進度條,顯示進度。奈何當時技術有限,查了許久也沒用找到解決方案,最后不了了之。
近來偶然想到這個問題,于是決定整理一下實現方式,也為和我曾經一樣碰壁的同學,提供一些思路。
1. 思路
1、首先我們這里實現的是一個實時的進度條,并不是一個純前端的進度條,它需要根據后端的處理進度來實時反饋進度條長度,那么必然要與后端交互。
當然這里容易陷入一個誤區(qū),覺得與后端交互的,那么這個功能的重點一定在后端,但實際上這個功能的重點在前端。
不難想到,我們要知道實時進度,那么一定需要不斷的請求后端,得到響應反饋,前后端請求比較常用的是ajax,但除它之外,我們還有更基礎的xhr(XMLHttpRequest)。作為后端同學可能對xhr有些陌生,實際上ajax就是基于xhr實現的。
2、xhr可以讓我們在不重新加載頁面的情況下更新網頁,在頁面已經加載后從后端請求并接受數據,這樣就可以無感的讓我們后端文件的上傳進度了。
3、為了監(jiān)聽文件上傳下載進度,我們主要使用到xhr的三個進度事件:
- progress: 在接收響應期間持續(xù)不斷地觸發(fā)
- load: 在接收到完整的響應數據時觸發(fā)
- error: 在請求發(fā)生錯誤時觸發(fā)
當然除上述三個事件之外,還有其他的進度事件,這不是本文的重點,大家可自行拓展學習XHR對象的進度事件
4、基于上述三個進度事件,我們可以通過process事件持續(xù)不斷地發(fā)送請求獲取文件上傳下載的進度,load事件用于文件上傳下載完成后的處理,比如提示成功。error用于請求發(fā)送錯誤時的處理。

5、有了上述的思路之后,我們來進行實際演示。
2. 實操
2.1 實現文件上傳實時進度條功能
1、創(chuàng)建springboot項目,引入spring web、lombok、文件上傳commons-fileupload依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
2、創(chuàng)建MultipartResolver的bean,用來將普通的請求封裝成擁有文件上傳功能的請求
@Component
public class FileUpLoadConfig {
@Bean(name="multipartResolver")
public MultipartResolver multipartResolver(){
return new CommonsMultipartResolver();
}
}
3、創(chuàng)建一個文件上傳接口:這里我單純做個演示,就直接在controller層中書寫了,實際生產要將上傳方法提取為工具類,在service中進行具體業(yè)務處理。
如下代碼為將文件上傳后,保存到資源文件夾下
@RestController
@RequestMapping("file")
public class FileController {
private final static Logger log = LoggerFactory.getLogger(FileController.class);
@PostMapping("/upload")
@ResponseBody
public ResponseEntity<String> fileUpload(@RequestParam("file") MultipartFile file) {
try {
// 獲取資源文件存放路徑,用于臨時存放生成的excel文件
String path = Objects.requireNonNull(this.getClass().getClassLoader().getResource("")).getPath();
// 文件名
String fileName = path + file.getOriginalFilename();
// 創(chuàng)建目標文件
File dest = new File(fileName);
// 向指定路徑寫入文件
file.transferTo(dest);
// 返回文件訪問路徑
return new ResponseEntity<>(fileName, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
log.info(String.format("文件上傳失敗,原因:%s",e));
return new ResponseEntity<>("文件上傳失敗", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
4、后端接口完成后,進入我們的重點,我們來實現前端進度條
5、首先引入jQuery,我這里使用了3.6.1版本

6、實現一個上傳的頁面,這里利用了html5的progress標簽,該標簽用于實現進度條,支持兩個屬性:value和max,分別為當前進度值和最大進度值
<div class="modal-body form ">
<!-- 文件上傳 -->
<form id="dialogForm" class="form-horizontal">
<div class="form-group">
<label class="control-label">文件:</label>
<div >
<input type="file" name="file" id="file" onchange="upload()">
</div>
</div>
<div class="form-group">
<label class="control-label">上傳進度:</label>
<div >
<!--進度條-->
<div id="progress-body">
<progress></progress>
<div id="progress-bar">0%</div>
</div>
</div>
</div>
</form>
</div>
7、書寫進度監(jiān)聽方法,即progress方法
//進度條更新
function progressHandle(e) {
$('#progress-body progress').attr({
value : e.loaded,
max : e.total
});
var percent = (e.loaded / e.total * 100).toFixed(2);
$('#progress-body #progress-bar').html(percent + "%");
};
8、書寫load,error方法
//上傳完成處理函數
function uploadSuccess(e) {
alert("上傳完成");
};
//上傳出錯處理函數
function uploadFail(e) {
alert("上傳失敗");
};
9、實現上傳方法upload
// 文件上傳
function upload() {
var formData = new FormData();
formData.append("file", $("#file")[0].files[0]);
$.ajax({
url : "/file/upload",
type : "POST",
data : formData,
processData : false, // 告訴jQuery不要去處理發(fā)送的數據
contentType : false, // 告訴jQuery不要去設置Content-Type請求頭
success : function(data) {
console.log(data);
},
xhr : function() {
var xhr = $.ajaxSettings.xhr();
// xhr.upload專用于上傳事件監(jiān)聽
if (xhr.upload) {
//處理進度條的事件
xhr.upload.addEventListener("progress", progressHandle,
false);
//加載完成的事件
xhr.addEventListener("load", uploadSuccess, false);
//加載出錯的事件
xhr.addEventListener("error", uploadFail, false);
return xhr;
}
}
});
}
10、運行項目,訪問上傳頁,我這里直接書寫在index.html中了

11、測試:如下圖所示,可以正常顯示進度

12、上傳成功后,后臺資源文件夾中也能看到對應的上傳文件,演示成功!

2.2 實現文件下載實時進度條功能
上述我們講解了如何實現上傳進度條功能,有了這個思路,我們再實現下載功能:
1、同樣,我們實現一個簡單的進度條頁面
<!-- 文件下載 -->
<form id="dialogForm" class="form-horizontal">
<div class="form-group">
<label class="control-label">下載進度:
</label>
<div>
<!--進度條-->
<div id="progress-body">
<progress></progress>
<div id="progress-bar">0%</div>
</div>
</div>
</div>
<button type="button" onclick="download()">下載</button>
</form>
2、實現下載方法
這里我們不再采用ajax的方法,而是直接通過xhr請求,并且因為要在瀏覽器中下載該文件,所以以window.URL.revokeObjectURL方法來下載并釋放該文件。
// 文件下載
function download() {
var xhr = new XMLHttpRequest();
//處理進度條的事件
xhr.addEventListener("progress", progressHandle, false);
//加載出錯的事件
xhr.addEventListener("error", uploadFail, false);
xhr.open("POST","/file/download");
//設置響應類型
xhr.responseType = 'blob';
xhr.onload = function (e) {
if (this.status === 200) {
// 截取掉'attachment;filename='
var filename = xhr.getResponseHeader("Content-disposition").slice(20);
var blob = this.response;
var a = document.createElement('a');
var url = URL.createObjectURL(blob);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
}
}
xhr.send();
}
//進度條更新
function progressHandle(e) {
$('#progress-body progress').attr({
value: e.loaded,
max: e.total
});
var percent = (e.loaded / e.total * 100).toFixed(2);
$('#progress-body #progress-bar').html(percent + "%");
};
//上傳出錯處理函數
function uploadFail(e) {
alert("下載失敗");
};
3、實現后端下載文件接口
這里與上傳文件不同的是,前端在進行文件上傳時,是可以獲取到文件的總大小的,而下載文件時因為是流式下載,前端是不知道要下載的文件一共有多少大小的。
因此也就無法估算總體的進度比例。所以我們后端接口中要通過Content-Length響應頭指定文件的總大小
我這里為了演示方便,直接下載上述上傳的文件。實際應用可更改為你自己的文件下載路徑。
@PostMapping("/download")
@ResponseBody
public ResponseEntity<String> download(HttpServletResponse response) throws IOException {
// 獲取資源文件存放路徑,用于臨時存放生成的excel文件
String path = Objects.requireNonNull(this.getClass().getClassLoader().getResource("")).getPath();
File pathFile = new File(path);
File[] files = pathFile.listFiles();
if (ObjectUtils.isEmpty(files)) {
return new ResponseEntity<>("文件為空,請先上傳文件", HttpStatus.OK);
}
InputStream inputStream = null;
ServletOutputStream ouputStream = null;
try {
for (File file : files) {
if(file.isDirectory()){
continue;
}
inputStream = new FileInputStream(file);
response.setContentType("application/x-msdownload");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
// 設置一個總長度,否則無法估算進度
response.setHeader("Content-Length",String.valueOf(file.length()));
ouputStream = response.getOutputStream();
byte b[] = new byte[1024];
int n;
while ((n = inputStream.read(b)) != -1) {
ouputStream.write(b, 0, n);
}
ouputStream.flush();
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(inputStream != null){
inputStream.close();
}
if(ouputStream != null){
ouputStream.close();
}
}
return new ResponseEntity<>("文件下載成功", HttpStatus.OK);
}
4、運行項目

5、測試:文件成功下載,進度也實時顯示

3. 項目源碼
以上演示源碼可在如下地址下載:
4. 總結
以上我們就完成了文件的上傳和下載的實時進度監(jiān)控,雖然這個功能的重點在前端,但是后端通過這個功能點,也能更好的理解前后端請求的交互。
最后我們拋出一個思考問題:如何實時監(jiān)控后端自定義功能的執(zhí)行進度?
以上就是SpringBoot實現文件上傳下載實時進度條功能(附源碼)的詳細內容,更多關于SpringBoot文件上傳下載實時進度條的資料請關注腳本之家其它相關文章!
相關文章
SpringMvc直接接收json數據自動轉化為Map的實例
今天小編就為大家分享一篇SpringMvc直接接收json數據自動轉化為Map的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08
IntelliJ IDEA下載GitHub私有倉庫到本地的方法(新版)
這篇文章主要介紹了IntelliJ IDEA下載GitHub私有倉庫到本地(新版),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05
java Object轉byte與byte轉Object方式
這篇文章主要介紹了java Object轉byte與byte轉Object方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08
SpringBoot整合MyBatis Plus實現基本CRUD與高級功能
Spring Boot是一款用于快速構建Spring應用程序的框架,而MyBatis Plus是MyBatis的增強工具,本文將詳細介紹如何在Spring Boot項目中整合MyBatis Plus,并展示其基本CRUD功能以及高級功能的實現方式,需要的朋友可以參考下2024-02-02

