springboot帶有進度條的上傳功能完整實例
本文實例講述了springboot帶有進度條的上傳功能。分享給大家供大家參考,具體如下:
一、說明
最近要做文件上傳,在網(wǎng)上找了很久都沒有一個全面的示例,特此記錄下來分享給大家。
1.文件上傳接口可按照springboot默認實現(xiàn),也可用commons-fileupload組件,本示例使用springboot默認文件上傳 2.最后也有commons-fileupload組件接口示例
2.重點在前端JS實現(xiàn)(也可以使用ajax上傳),參考了網(wǎng)上大量上傳文件顯示進度條博客以及技術(shù)方案,在此做了一個統(tǒng)一的總結(jié),方便后續(xù)使用
3.這僅僅是一個示例,大家可根據(jù)實際需要改進。
二、前端代碼
<!DOCTYPE html>
<html>
<meta charset="UTF-8" />
<head>
<title>文件上傳</title>
<link rel="external nofollow"
rel="stylesheet">
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
</head>
<body class="container">
<br />
<span id="time"></span>
<div class="row">
<input class="btn btn-info btn-xs" type="file" name="file" /><br />
<div class="col-lg-5"
style="padding-left: 0; padding-right: 0; margin-bottom: 0px;">
<div class="progress progress-striped active" style="display: ">
<div id="progressBar" class="progress-bar progress-bar-success"
role="progressbar" aria-valuemin="0" aria-valuenow="0"
aria-valuemax="100" style="width: 20%"></div>
</div>
</div>
<!-- 顯示上傳速度 -->
<div id="showInfo" class="col-lg-2">0KB/s</div>
</div>
<!-- 顯示文件信息 -->
<div id="showFieInfo" class="row">
<label name="upfileName"></label><br />
<label name="upfileSize"></label><br />
<label name="upfileType"></label><br />
</div>
<div class="row">
<input class="btn btn-success btn-xs" type="button" name="upload" value="上傳" />
<input class="btn btn-success btn-xs" type="button" name="cancelUpload" value="取消" />
</div>
</body>
<script type="text/javascript">
var fileBtn = $("input[name=file]");
var processBar= $("#progressBar");
var uploadBtn=$("input[name=upload]");
var canelBtn=$("input[name=cancelUpload]");
var ot;//上傳開始時間
var oloaded;//已上傳文件大小
fileBtn.change(function() {
var fileObj = fileBtn.get(0).files[0]; //js獲取文件對象
if (fileObj) {
var fileSize = getSize(fileObj.size);
$("label[name=upfileName]").text('文件名:' + fileObj.name);
$("label[name=upfileSize]").text('文件大?。? + fileSize);
$("label[name=upfileType]").text('文件類型:' + fileObj.type);
uploadBtn.attr('disabled', false);
}
});
// 上傳文件按鈕點擊的時候
uploadBtn.click(function(){
// 進度條歸零
setProgress(0);
// 上傳按鈕禁用
$(this).attr('disabled', true);
// 進度條顯示
showProgress();
// 上傳文件
uploadFile();
});
function uploadFile(){
var url ="/to/upload";
var fileObj = fileBtn.get(0).files[0];
if(fileObj==null){
alert("請選擇文件");
return;
}
// FormData 對象
var form = new FormData();
form.append('file', fileObj); // 文件對象
// XMLHttpRequest 對象
var xhr = new XMLHttpRequest();
//true為異步處理
xhr.open('post', url, true);
//上傳開始執(zhí)行方法
xhr.onloadstart = function() {
console.log('開始上傳')
ot = new Date().getTime(); //設置上傳開始時間
oloaded = 0;//已上傳的文件大小為0
};
xhr.upload.addEventListener('progress', progressFunction, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.send(form);
function progressFunction(evt) {
debugger;
if (evt.lengthComputable) {
var completePercent = Math.round(evt.loaded / evt.total * 100)
+ '%';
processBar.width(completePercent);
processBar.text(completePercent);
var time = $("#time");
var nt = new Date().getTime(); //獲取當前時間
var pertime = (nt-ot)/1000; //計算出上次調(diào)用該方法時到現(xiàn)在的時間差,單位為s
ot = new Date().getTime(); //重新賦值時間,用于下次計算
var perload = evt.loaded - oloaded; //計算該分段上傳的文件大小,單位b
oloaded = evt.loaded; //重新賦值已上傳文件大小
//上傳速度計算
var speed = perload/pertime;//單位b/s
var bspeed = speed;
var units = 'b/s';//單位名稱
if(speed/1024>1){
speed = speed/1024;
units = 'k/s';
}
if(speed/1024>1){
speed = speed/1024;
units = 'M/s';
}
speed = speed.toFixed(1);
//剩余時間
var resttime = ((evt.total-evt.loaded)/bspeed).toFixed(1);
$("#showInfo").html(speed+units+',剩余時間:'+resttime+'s');
}
}
//上傳成功后回調(diào)
function uploadComplete(evt) {
uploadBtn.attr('disabled', false);
console.log('上傳完成')
};
//上傳失敗回調(diào)
function uploadFailed(evt) {
console.log('上傳失敗' + evt.target.responseText);
}
//終止上傳
function cancelUpload() {
xhr.abort();
}
//上傳取消后回調(diào)
function uploadCanceled(evt) {
console.log('上傳取消,上傳被用戶取消或者瀏覽器斷開連接:' + evt.target.responseText);
}
canelBtn.click(function(){
uploadBtn.attr('disabled', false);
cancelUpload();
})
}
function getSize(size) {
var fileSize = '0KB';
if (size > 1024 * 1024) {
fileSize = (Math.round(size / (1024 * 1024))).toString() + 'MB';
} else {
fileSize = (Math.round(size / 1024)).toString() + 'KB';
}
return fileSize;
}
function setProgress(w) {
processBar.width(w + '%');
}
function showProgress() {
processBar.parent().show();
}
function hideProgress() {
processBar.parent().hide();
}
</script>
</html>
效果:

三、對上傳代碼進行組件化封裝
UploadCommon.js
/**
* 上傳文件公共組件
*
* @param url 上傳地址
* @param processBar 進度條 jquery獲取的頁面組件
* @param speedLab 顯示上傳速度Label jquery獲取的頁面組件
* @param uploadBtn 上傳按鈕 jquery獲取的頁面組件
* @param cancelBtn 取消上傳按鈕 jquery獲取的頁面組件
* @param callBack 上傳完成回調(diào)函數(shù) 上傳完成后的回調(diào)函數(shù),可以不傳
* @author
* @returns
*/
function UploadCommon(url, processBar, speedLab, uploadBtn, cancelBtn, callBack){
function init() {
// 每次回調(diào)監(jiān)測上傳開始時間
var startTime = null
// 已上傳文件大小
var oloaded = null
var xhr = new XMLHttpRequest()
function setProgress(w) {
processBar.width(w + '%');
processBar.text(w + '%');
}
function progressMonitor(evt){
if (evt.lengthComputable) {
var completePercent = Math.round(evt.loaded / evt.total * 100)
setProgress(completePercent)
var nowTime = new Date().getTime()
// 計算出上次調(diào)用該方法時到現(xiàn)在的時間差,單位為s
var pertime = (nowTime - startTime) / 1000
// 重新賦值時間,用于下次計算
startTime = new Date().getTime()
// 計算該分段上傳的文件大小,單位b
var perload = evt.loaded - oloaded
// 重新賦值已上傳文件大小,用以下次計算
oloaded = evt.loaded
// 上傳速度計算,單位b/s
var speed = perload / pertime
var bspeed = speed
// 單位名稱
var units = 'bit/s'if (speed / 1024 > 1) {
speed = speed / 1024
units = 'Kb/s'
}
if (speed / 1024 > 1) {
speed = speed / 1024
units = 'Mb/s'
}
speed = speed.toFixed(1);
// 剩余時間
var resttime = ((evt.total - evt.loaded) / bspeed).toFixed(1)
speedLab.html(speed + units + ',剩余時間:' + resttime + 's')
}
}
// 上傳成功后回調(diào)
function uploadComplete(evt) {
uploadBtn.attr('disabled', false)
var status = evt.currentTarget.status
if (status == 401) {
alert('請登錄后再上傳')
return
}
if (status == 403) {
alert('無權(quán)限操作')
return
}
if (status != 200) {
alert('上傳異常,錯誤碼' + status)
return
}
var response=JSON.parse(evt.currentTarget.response)
if (response.code!='200') {
alert('上傳處理異常' + response.msg)
return
}
console.log('上傳成功')
if ( callBack != null && typeof callBack != 'undefined') {
callBack()
}
};
// 上傳失敗回調(diào)
function uploadFailed(evt) {
alert('上傳處理失敗' + evt.target.responseText)
}
// 終止上傳
function cancelUpload() {
xhr.abort()
}
// 上傳取消后回調(diào)
function uploadCanceled(evt) {
alert('文件上傳已終止:' + evt.target.responseText)
}
// 添加取消上傳事件
cancelBtn.click(function() {
uploadBtn.attr('disabled', false)
cancelUpload();
})
this.uploadFile = function(formData) {
// 上傳按鈕禁用
uploadBtn.attr('disabled', true);
setProgress(0)
// true為異步處理
xhr.open('post', url, true)
// 上傳開始執(zhí)行方法
xhr.onloadstart = function() {
console.log('開始上傳')
// 設置上傳開始時間
startTime = new Date().getTime()
// 已上傳的文件大小為0
oloaded = 0
}
xhr.upload.addEventListener('progress', progressMonitor, false)
xhr.addEventListener("load", uploadComplete, false)
xhr.addEventListener("error", uploadFailed, false)
xhr.addEventListener("abort", uploadCanceled, false)
xhr.send(formData);
}
this.setProgressValue=function(w){
processBar.width(w + '%')
processBar.text(w + '%')
}
}
return new init()
}
調(diào)用
$(document).ready(function() {
var addVersionBtn=$('#addVersionBtn') //<button>
var cancelUploadBtn=$('#cancelUploadBtn') //<button>
var fileInput=$("#filewareFile") //<input>
var processBar = $("#progressBar"); //div
var fileNameLab=$("label[name=upfileName]") //<label>
var fileSizeLab=$("label[name=upfileSize]") //...
var fileTypeLab=$("label[name=upfileType]") //...
var speedLab=$("#showSpeed") //<label>
var url='/api/file'
//獲取文件上傳實例
var upload=UploadCommon(url,processBar,speedLab,addVersionBtn,cancelUploadBtn,initPageInfo)
// 文件選擇框變更事件
fileInput.change(function() {
var fileObj = fileInput.get(0).files[0]; // js獲取文件對象
if (fileObj) {
var fileSize = getSize(fileObj.size);
fileNameLab.text('文件名:' + fileObj.name);
fileSizeLab.text('文件大小:' + fileSize);
fileTypeLab.text('文件類型:' + fileObj.type);
addVersionBtn.attr('disabled', false);
}
});
// 點擊上傳固件事件
addVersionBtn.click(function(){
var versionInfo=$('#version').val()
var file = fileInput.get(0).files[0]
var strategyInfo=$('#versionType').val()
if(file==null){
alert("固件文件不能為空")
return
}
if(versionInfo==''){
alert("版本號不能為空")
return
}
if(strategyInfo==''){
alert("升級策略不能為空")
return
}
// 創(chuàng)建提交數(shù)據(jù)
var formData = new FormData();
formData.append('firmFile', fileInput.get(0).files[0]);
formData.append('version', versionInfo);
formData.append('strategy', strategyInfo);
// 上傳文件
upload.uploadFile(formData)
})
});
四,服務端接口
1.springboot默認實現(xiàn)
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 添加Swagger2依賴,用于生成接口文檔 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<!--end-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server: port: 8080 tomcat: uri-encoding: UTF-8 application: name: demo thymeleaf: encoding: UTF-8 cache: true mode: LEGACYHTML5 devtools: restart: enabled: true http: multipart: maxFileSize: 500Mb maxRequestSize: 500Mb location: D:/tmp debug: false
接口:
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
if (file == null || file.isEmpty()) {
return "file is empty";
}
// 獲取文件名
String fileName = file.getOriginalFilename();
// 文件存儲路徑
String filePath = "D:/data/" + UUID.randomUUID().toString().replaceAll("-", "") + "_" + fileName;
logger.info("save file to:" + filePath);
File dest = new File(filePath);
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
try {
file.transferTo(dest);
return "success";
} catch (Exception e) {
e.printStackTrace();
}
return "fail";
}
啟動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.使用commons-fileupload上傳組件
application.yml
server: port: 8080 tomcat: uri-encoding : UTF-8 spring: application: name: svc-demo thymeleaf: encoding: UTF-8 cache: false mode: LEGACYHTML5 debug: false
pom .xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--添加文件上傳支持-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!--添加html5支持-->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
進程類
public class Progress{
private long bytesRead; //已讀取文件的比特數(shù)
private long contentLength;//文件總比特數(shù)
private long items; //正讀的第幾個文件
public long getBytesRead(){
return bytesRead;
}
public void setBytesRead(long bytesRead){
this.bytesRead = bytesRead;
}
public long getContentLength() {
return contentLength;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
public long getItems() {
return items;
}
public void setItems(long items)\{
this.items = items;
}
}
監(jiān)聽類
@Component
public class FileUploadProgressListener implements ProgressListener{
private HttpSession session;
public void setSession(HttpSession session){
this.session=session;
Progress status = new Progress();//保存上傳狀態(tài)
session.setAttribute("status", status);
}
@Override
public void update(long bytesRead, long contentLength, int items) {
Progress status = (Progress) session.getAttribute("status");
status.setBytesRead(bytesRead);
status.setContentLength(contentLength);
status.setItems(items);
}
}
文件上傳處理類
public class CustomMultipartResolver extends CommonsMultipartResolver{
// 注入第二步寫的FileUploadProgressListener
@Autowired
private FileUploadProgressListener progressListener;
public void setFileUploadProgressListener(FileUploadProgressListener progressListener){
this.progressListener = progressListener;
}
@Override
public MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException{
String encoding = determineEncoding(request);
FileUpload fileUpload = prepareFileUpload(encoding);
//fileUpload.setFileSizeMax(1024 * 1024 * 500);// 單個文件最大500M
//fileUpload.setSizeMax(1024 * 1024 * 500);// 一次提交總文件最大500M
progressListener.setSession(request.getSession());// 問文件上傳進度監(jiān)聽器設置session用于存儲上傳進度
fileUpload.setProgressListener(progressListener);// 將文件上傳進度監(jiān)聽器加入到 fileUpload 中
try{
List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
return parseFileItems(fileItems, encoding);
} catch (FileUploadBase.SizeLimitExceededException ex) {
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
} catch (FileUploadException ex){
throw new MultipartException("Could not parse multipart servlet request", ex);
}
}
}
控制器
@RestController
public class FileController{
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return "文件為空";
}
// 獲取文件名
String fileName = file.getOriginalFilename();// 文件上傳后的路徑
// 文件上傳后的路徑
String filePath = null;
try{
filePath = new File("").getCanonicalPath() + "/tmp/uploadFile/";
} catch (IOException e){
e.printStackTrace();
}
//存儲路徑
String tagFilePath = filePath + CommonUtil.getCurrentTime() + fileName;
File dest = new File(tagFilePath);
// 檢測是否存在目錄
if (!dest.getParentFile().exists()){
dest.getParentFile().mkdirs();
}
try{
file.transferTo(dest);
} catch (IllegalStateException e){
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
return fileName + "上傳失敗";
}
}
啟動類
//注意取消自動Multipart配置,否則可能在上傳接口中拿不到file的值
@EnableAutoConfiguration(exclude = { MultipartAutoConfiguration.class })
@SpringBootApplication
public class Application extends SpringBootServletInitializer{
//注入自定義的文件上傳處理類
@Bean(name = "multipartResolver")
public MultipartResolver multipartResolver() {
CustomMultipartResolver customMultipartResolver = new CustomMultipartResolver();
return customMultipartResolver;
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application){
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
更多關(guān)于java相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Spring框架入門與進階教程》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對大家java程序設計有所幫助。
相關(guān)文章
Springboot基于Redisson實現(xiàn)Redis分布式可重入鎖源碼解析
這篇文章主要介紹了Springboot基于Redisson實現(xiàn)Redis分布式可重入鎖,本文通過案例源碼分析給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03
SpringBoot+Redis隊列實現(xiàn)Java版秒殺的示例代碼
本文主要介紹了SpringBoot+Redis隊列實現(xiàn)Java版秒殺的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-06-06
java使用jdbc連接數(shù)據(jù)庫工具類和jdbc連接mysql數(shù)據(jù)示例
這篇文章主要介紹了java使用jdbc連接數(shù)據(jù)庫的工具類和使用jdbc連接mysql數(shù)據(jù)的示例,需要的朋友可以參考下2014-03-03

