您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)如何實(shí)現(xiàn)springboot帶有進(jìn)度條的上傳功能,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
具體如下:
一、說(shuō)明
最近要做文件上傳,在網(wǎng)上找了很久都沒(méi)有一個(gè)全面的示例,特此記錄下來(lái)分享給大家。
1.文件上傳接口可按照springboot默認(rèn)實(shí)現(xiàn),也可用commons-fileupload組件,本示例使用springboot默認(rèn)文件上傳 2.最后也有commons-fileupload組件接口示例
2.重點(diǎn)在前端JS實(shí)現(xiàn)(也可以使用ajax上傳),參考了網(wǎng)上大量上傳文件顯示進(jìn)度條博客以及技術(shù)方案,在此做了一個(gè)統(tǒng)一的總結(jié),方便后續(xù)使用
3.這僅僅是一個(gè)示例,大家可根據(jù)實(shí)際需要改進(jìn)。
二、前端代碼
<!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> <p class="row"> <input class="btn btn-info btn-xs" type="file" name="file" /><br /> <p class="col-lg-5" > <p class="progress progress-striped active" > <p id="progressBar" class="progress-bar progress-bar-success" role="progressbar" aria-valuemin="0" aria-valuenow="0" aria-valuemax="100" ></p> </p> </p> <!-- 顯示上傳速度 --> <p id="showInfo" class="col-lg-2">0KB/s</p> </p> <!-- 顯示文件信息 --> <p id="showFieInfo" class="row"> <label name="upfileName"></label><br /> <label name="upfileSize"></label><br /> <label name="upfileType"></label><br /> </p> <p 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="取消" /> </p></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;//上傳開(kāi)始時(shí)間 var oloaded;//已上傳文件大小 fileBtn.change(function() { var fileObj = fileBtn.get(0).files[0]; //js獲取文件對(duì)象 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); } }); // 上傳文件按鈕點(diǎn)擊的時(shí)候 uploadBtn.click(function(){ // 進(jìn)度條歸零 setProgress(0); // 上傳按鈕禁用 $(this).attr('disabled', true); // 進(jìn)度條顯示 showProgress(); // 上傳文件 uploadFile(); }); function uploadFile(){ var url ="/to/upload"; var fileObj = fileBtn.get(0).files[0]; if(fileObj==null){ alert("請(qǐng)選擇文件"); return; } // FormData 對(duì)象 var form = new FormData(); form.append('file', fileObj); // 文件對(duì)象 // XMLHttpRequest 對(duì)象 var xhr = new XMLHttpRequest(); //true為異步處理 xhr.open('post', url, true); //上傳開(kāi)始執(zhí)行方法 xhr.onloadstart = function() { console.log('開(kāi)始上傳') ot = new Date().getTime(); //設(shè)置上傳開(kāi)始時(shí)間 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(); //獲取當(dāng)前時(shí)間 var pertime = (nt-ot)/1000; //計(jì)算出上次調(diào)用該方法時(shí)到現(xiàn)在的時(shí)間差,單位為s ot = new Date().getTime(); //重新賦值時(shí)間,用于下次計(jì)算 var perload = evt.loaded - oloaded; //計(jì)算該分段上傳的文件大小,單位b oloaded = evt.loaded; //重新賦值已上傳文件大小 //上傳速度計(jì)算 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); //剩余時(shí)間 var resttime = ((evt.total-evt.loaded)/bspeed).toFixed(1); $("#showInfo").html(speed+units+',剩余時(shí)間:'+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('上傳取消,上傳被用戶取消或者瀏覽器斷開(kāi)連接:' + 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>
三、對(duì)上傳代碼進(jìn)行組件化封裝
UploadCommon.js
/** * 上傳文件公共組件 * * @param url 上傳地址 * @param processBar 進(jìn)度條 jquery獲取的頁(yè)面組件 * @param speedLab 顯示上傳速度Label jquery獲取的頁(yè)面組件 * @param uploadBtn 上傳按鈕 jquery獲取的頁(yè)面組件 * @param cancelBtn 取消上傳按鈕 jquery獲取的頁(yè)面組件 * @param callBack 上傳完成回調(diào)函數(shù) 上傳完成后的回調(diào)函數(shù),可以不傳 * @author * @returns */function UploadCommon(url, processBar, speedLab, uploadBtn, cancelBtn, callBack){ function init() { // 每次回調(diào)監(jiān)測(cè)上傳開(kāi)始時(shí)間 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() // 計(jì)算出上次調(diào)用該方法時(shí)到現(xiàn)在的時(shí)間差,單位為s var pertime = (nowTime - startTime) / 1000 // 重新賦值時(shí)間,用于下次計(jì)算 startTime = new Date().getTime() // 計(jì)算該分段上傳的文件大小,單位b var perload = evt.loaded - oloaded // 重新賦值已上傳文件大小,用以下次計(jì)算 oloaded = evt.loaded // 上傳速度計(jì)算,單位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); // 剩余時(shí)間 var resttime = ((evt.total - evt.loaded) / bspeed).toFixed(1) speedLab.html(speed + units + ',剩余時(shí)間:' + resttime + 's') } } // 上傳成功后回調(diào) function uploadComplete(evt) { uploadBtn.attr('disabled', false) var status = evt.currentTarget.status if (status == 401) { alert('請(qǐng)登錄后再上傳') return } if (status == 403) { alert('無(wú)權(quán)限操作') return } if (status != 200) { alert('上傳異常,錯(cuò)誤碼' + 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) // 上傳開(kāi)始執(zhí)行方法 xhr.onloadstart = function() { console.log('開(kāi)始上傳') // 設(shè)置上傳開(kāi)始時(shí)間 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"); //p var fileNameLab=$("label[name=upfileName]") //<label> var fileSizeLab=$("label[name=upfileSize]") //... var fileTypeLab=$("label[name=upfileType]") //... var speedLab=$("#showSpeed") //<label> var url='/api/file' //獲取文件上傳實(shí)例 var upload=UploadCommon(url,processBar,speedLab,addVersionBtn,cancelUploadBtn,initPageInfo) // 文件選擇框變更事件 fileInput.change(function() { var fileObj = fileInput.get(0).files[0]; // js獲取文件對(duì)象 if (fileObj) { var fileSize = getSize(fileObj.size); fileNameLab.text('文件名:' + fileObj.name); fileSizeLab.text('文件大?。?#39; + fileSize); fileTypeLab.text('文件類型:' + fileObj.type); addVersionBtn.attr('disabled', false); } }); // 點(diǎn)擊上傳固件事件 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("版本號(hào)不能為空") return } if(strategyInfo==''){ alert("升級(jí)策略不能為空") 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) })});
四,服務(wù)端接口
1.springboot默認(rèn)實(shí)現(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:/tmpdebug: false
接口:
@PostMapping("/upload")public String uploadFile(@RequestParam("file") MultipartFile file) { if (file == null || file.isEmpty()) { return "file is empty"; } // 獲取文件名 String fileName = file.getOriginalFilename(); // 文件存儲(chǔ)路徑 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"; }
啟動(dòng)類
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@EnableTransactionManagementpublic 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-8spring: application: name: svc-demo thymeleaf: encoding: UTF-8 cache: false mode: LEGACYHTML5debug: 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>
進(jìn)程類
public class Progress{ private long bytesRead; //已讀取文件的比特?cái)?shù) private long contentLength;//文件總比特?cái)?shù) private long items; //正讀的第幾個(gè)文件 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)聽(tīng)類
@Componentpublic 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{ // 注入第二步寫(xiě)的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);// 單個(gè)文件最大500M //fileUpload.setSizeMax(1024 * 1024 * 500);// 一次提交總文件最大500M progressListener.setSession(request.getSession());// 問(wèn)文件上傳進(jìn)度監(jiān)聽(tīng)器設(shè)置session用于存儲(chǔ)上傳進(jìn)度 fileUpload.setProgressListener(progressListener);// 將文件上傳進(jìn)度監(jiān)聽(tīng)器加入到 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); } }}
控制器
@RestControllerpublic 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(); } //存儲(chǔ)路徑 String tagFilePath = filePath + CommonUtil.getCurrentTime() + fileName; File dest = new File(tagFilePath); // 檢測(cè)是否存在目錄 if (!dest.getParentFile().exists()){ dest.getParentFile().mkdirs(); } try{ file.transferTo(dest); } catch (IllegalStateException e){ e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); } return fileName + "上傳失敗"; }}
啟動(dòng)類
//注意取消自動(dòng)Multipart配置,否則可能在上傳接口中拿不到file的值@EnableAutoConfiguration(exclude = { MultipartAutoConfiguration.class })@SpringBootApplicationpublic 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)于“如何實(shí)現(xiàn)springboot帶有進(jìn)度條的上傳功能”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。