您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關springboot如何實現(xiàn)帶有進度條的上傳功能的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
一、說明
最近要做文件上傳,在網(wǎng)上找了很久都沒有一個全面的示例,特此記錄下來分享給大家。
1.文件上傳接口可按照springboot默認實現(xiàn),也可用commons-fileupload組件,本示例使用springboot默認文件上傳 2.最后也有commons-fileupload組件接口示例
2.重點在前端JS實現(xiàn)(也可以使用ajax上傳),參考了網(wǎng)上大量上傳文件顯示進度條博客以及技術方案,在此做了一個統(tǒng)一的總結(jié),方便后續(xù)使用
3.這僅僅是一個示例,大家可根據(jù)實際需要改進。
二、前端代碼
<!DOCTYPE html> <html> <meta charset="UTF-8" /> <head> <title>文件上傳</title> <link href="https://cdn.bootcss.com/bootstrap/3.3.2/css/bootstrap.css" 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" > <div class="progress progress-striped active" > <div id="progressBar" class="progress-bar progress-bar-success" role="progressbar" aria-valuemin="0" aria-valuenow="0" aria-valuemax="100" ></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('文件大?。?#39; + 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('無權限操作') 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('文件大?。?#39; + 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); }
感謝各位的閱讀!關于“springboot如何實現(xiàn)帶有進度條的上傳功能”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。